Skip to content

Commit 68ff7a9

Browse files
wip v28
1 parent b51fe93 commit 68ff7a9

9 files changed

Lines changed: 139 additions & 31 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,5 @@ uapi/tailwind.config.js
8989

9090
# Miscellaneous
9191
.tmp/
92+
.vercel
93+
.env*.local

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"private": true,
33
"name": "bitcode-monorepo",
44
"version": "0.0.0",
5+
"packageManager": "pnpm@10.33.0",
56
"engines": {
67
"node": ">=21"
78
},

packages/agent-generics/src/substeps/factories.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ export function factoryReason<T>(): Executor<T, T & { reasoning: Reasoning }> {
683683
GenerationSubMetaSubStep.REASON,
684684
{
685685
buildUserPrompt: (input) => {
686-
const typedInput = input as any;
686+
const typedInput = input && typeof input === 'object' ? input as any : {};
687687
// Check context to provide appropriate reasoning prompt
688688
const isStitch = typedInput.partialOutput !== undefined;
689689
const isSum = typedInput.chunkResults !== undefined;
@@ -694,7 +694,7 @@ export function factoryReason<T>(): Executor<T, T & { reasoning: Reasoning }> {
694694
if (isSum) {
695695
return `Reason about how to combine these chunk results:\n\n${JSON.stringify(typedInput.chunkResults, null, 2)}`;
696696
}
697-
return `Apply logical reasoning to solve:\n\n${JSON.stringify(input, null, 2)}`;
697+
return `Apply logical reasoning to solve:\n\n${JSON.stringify(input ?? null, null, 2)}`;
698698
},
699699

700700
parseOutput: async (output, input) => {

packages/pipeline-hosts/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ return `blocked_readiness` rather than a worthy fit.
3838
The exported evidence must include the AssetPack embedding policy
3939
(`text-embedding-3-small`, `1536` dimensions, cosine
4040
`match_deliverable_vectors`) so SQL readback can detect vector-space drift.
41+
The harness prepares the repo-pinned `pnpm` runtime before installing so
42+
Corepack's latest release cannot drift the frozen lockfile contract. Runtime
43+
helpers used only by the harness are installed under `.bitcode/pipeline-harness`
44+
so historical deposited source revisions do not need to carry newer harness
45+
dependencies.
4146

4247
Structured database telemetry is part of the harness contract. A real Read/Fit
4348
pipeline run must write the deliverable hierarchy:

packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,13 @@ describe('asset-pack sandbox harness plan', () => {
7070
'runtime-readiness',
7171
'package-manager-readiness',
7272
'workspace-install',
73+
'harness-runtime-install',
7374
'asset-pack-pipeline-run',
7475
]);
76+
expect(plan.commands.find((command) => command.label === 'package-manager-readiness')).toMatchObject({
77+
cmd: 'corepack',
78+
args: ['prepare', 'pnpm@10.33.0', '--activate'],
79+
});
7580
});
7681

7782
it('generates a syntactically valid live pipeline runner', () => {

packages/pipeline-hosts/src/asset-pack-harness.ts

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ const HOST_SMOKE_RUNNER_PATH = `${HARNESS_DIRECTORY}/run-host-smoke.mjs`;
2020
const LIVE_PIPELINE_RUNNER_PATH = `${HARNESS_DIRECTORY}/run-live-asset-pack-pipeline.ts`;
2121
const EVIDENCE_PATH = `${HARNESS_DIRECTORY}/evidence.json`;
2222
const TELEMETRY_PATH = `${HARNESS_DIRECTORY}/telemetry.jsonl`;
23+
const TSCONFIG_PATHS_REGISTER_PATH = `${HARNESS_DIRECTORY}/node_modules/tsconfig-paths/register`;
2324
const SANDBOX_WORKING_DIRECTORY = '/vercel/sandbox';
2425
const DEFAULT_LONG_TIMEOUT_MS = 45 * 60 * 1000;
26+
const SANDBOX_PNPM_VERSION = '10.33.0';
2527

2628
export interface BuildAssetPackSandboxHarnessOptions {
2729
mode?: PipelineHarnessMode;
@@ -116,7 +118,7 @@ function buildCommands(
116118
commands.push({
117119
label: 'package-manager-readiness',
118120
cmd: 'corepack',
119-
args: ['enable'],
121+
args: ['prepare', `pnpm@${SANDBOX_PNPM_VERSION}`, '--activate'],
120122
required: true,
121123
});
122124

@@ -129,6 +131,13 @@ function buildCommands(
129131
});
130132
}
131133

134+
commands.push({
135+
label: 'harness-runtime-install',
136+
cmd: 'npm',
137+
args: ['install', '--prefix', HARNESS_DIRECTORY, 'tsconfig-paths@4.2.0'],
138+
required: true,
139+
});
140+
132141
commands.push({
133142
label: 'asset-pack-pipeline-run',
134143
cmd: 'pnpm',
@@ -137,6 +146,10 @@ function buildCommands(
137146
'@bitcode/pipeline-hosts',
138147
'exec',
139148
'ts-node',
149+
'--project',
150+
'../../tsconfig.json',
151+
'-r',
152+
`../../${TSCONFIG_PATHS_REGISTER_PATH}`,
140153
'--transpile-only',
141154
`../../${LIVE_PIPELINE_RUNNER_PATH}`,
142155
],
@@ -229,18 +242,16 @@ await writeFile(\`\${artifactDir}/evidence.json\`, JSON.stringify(evidence, null
229242
function createLiveAssetPackPipelineRunner(): string {
230243
return `import { mkdir, readFile, writeFile } from 'node:fs/promises';
231244
import { createHash, randomUUID } from 'node:crypto';
232-
import { assetPackPipeline } from '../../packages/pipelines/asset-pack/src/index';
233-
import { enablePipelineStreaming, factoryPipelineExecution } from '../../packages/pipelines-generics/src/index';
234245
235246
const manifestPath = process.env.BITCODE_PIPELINE_HARNESS_MANIFEST || '${MANIFEST_PATH}';
236247
const artifactDir = process.env.BITCODE_PIPELINE_HARNESS_ARTIFACT_DIR || '${HARNESS_DIRECTORY}';
237-
const manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
238248
const runId = process.env.BITCODE_PIPELINE_RUN_ID || randomUUID();
239249
const DEFAULT_USER_ID = '00000000-0000-4000-8000-000000000000';
240250
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
241-
let userId = process.env.BITCODE_PIPELINE_USER_ID || manifest.deposit?.userId || DEFAULT_USER_ID;
242251
const startedAt = new Date().toISOString();
243-
const manifestRoot = createHash('sha256').update(JSON.stringify(manifest)).digest('hex');
252+
let manifest = null;
253+
let manifestRoot = null;
254+
let userId = process.env.BITCODE_PIPELINE_USER_ID || DEFAULT_USER_ID;
244255
const events = [];
245256
let resultState = 'blocked_readiness';
246257
let output = null;
@@ -334,14 +345,14 @@ async function insertPipelineRun() {
334345
started_at: startedAt,
335346
metadata: {
336347
bitcodePipelineHarness: true,
337-
harnessMode: manifest.harnessMode,
348+
harnessMode: manifest?.harnessMode || 'asset_pack_pipeline',
338349
manifestRoot,
339-
sourceRevision: manifest.sourceRevision,
350+
sourceRevision: manifest?.sourceRevision || null,
340351
},
341352
input: {
342-
read: manifest.read,
343-
deposit: manifest.deposit,
344-
sourceRevision: manifest.sourceRevision,
353+
read: manifest?.read || null,
354+
deposit: manifest?.deposit || null,
355+
sourceRevision: manifest?.sourceRevision || null,
345356
},
346357
}).select('id').single();
347358
if (insertError) {
@@ -444,17 +455,25 @@ async function insertHarnessStreamLog(status) {
444455
status,
445456
resultState,
446457
manifestRoot,
447-
harnessMode: manifest.harnessMode,
458+
harnessMode: manifest?.harnessMode || 'asset_pack_pipeline',
448459
},
449460
});
450461
} catch (persistError) {
451462
record({ type: 'stream-log-persist-blocked', stage: 'telemetry-readback', error: persistError?.message || String(persistError) });
452463
}
453464
}
454465
466+
async function main() {
455467
await mkdir(artifactDir, { recursive: true });
456468
457469
try {
470+
manifest = JSON.parse(await readFile(manifestPath, 'utf8'));
471+
manifestRoot = createHash('sha256').update(JSON.stringify(manifest)).digest('hex');
472+
userId = process.env.BITCODE_PIPELINE_USER_ID || manifest.deposit?.userId || DEFAULT_USER_ID;
473+
const [{ assetPackPipeline }, { enablePipelineStreaming, factoryPipelineExecution }] = await Promise.all([
474+
import('../../packages/pipelines/asset-pack/src/index'),
475+
import('../../packages/pipelines-generics/src/index'),
476+
]);
458477
const execution = factoryPipelineExecution('asset_pack', undefined, {
459478
pipelineName: 'asset_pack',
460479
family: 'asset_pack',
@@ -548,7 +567,7 @@ try {
548567
549568
const evidence = {
550569
schema: 'bitcode.pipeline-harness.evidence',
551-
harnessMode: manifest.harnessMode,
570+
harnessMode: manifest?.harnessMode || 'asset_pack_pipeline',
552571
resultState: 'blocked_readiness',
553572
resultReasons: [
554573
'AssetPack pipeline execution did not produce admissible commercial result evidence.',
@@ -577,5 +596,12 @@ try {
577596
await insertHarnessStreamLog(process.exitCode ? 'failed' : 'completed');
578597
await writeFile(\`\${artifactDir}/telemetry.jsonl\`, events.map((event) => JSON.stringify(event)).join('\\n') + '\\n');
579598
}
599+
600+
}
601+
602+
main().catch((caught) => {
603+
process.stderr.write(\`\${caught?.message || String(caught)}\\n\`);
604+
process.exitCode = 1;
605+
});
580606
`;
581607
}

packages/pipeline-hosts/src/dev/run-asset-pack-sandbox-harness.ts

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,36 @@ const TRUSTED_SANDBOX_ENV_KEYS = [
2222
'BITCODE_PIPELINE_USER_ID',
2323
] as const;
2424

25+
const REDACTED_OUTPUT_ENV_KEYS = [
26+
...TRUSTED_SANDBOX_ENV_KEYS,
27+
'BITCODE_SANDBOX_SOURCE_GIT_PASSWORD',
28+
'GITHUB_TOKEN',
29+
'GITHUB_PAT',
30+
'GH_TOKEN',
31+
'VERCEL_OIDC_TOKEN',
32+
'VERCEL_TOKEN',
33+
] as const;
34+
2535
function loadLocalEnvFiles(): void {
26-
for (const relativePath of ['.env.local', 'uapi/.env.local']) {
27-
const path = resolve(process.cwd(), relativePath);
28-
if (!existsSync(path)) continue;
29-
const body = readFileSync(path, 'utf8');
30-
for (const line of body.split(/\r?\n/)) {
31-
const trimmed = line.trim();
32-
if (!trimmed || trimmed.startsWith('#')) continue;
33-
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed);
34-
if (!match) continue;
35-
const [, key, rawValue] = match;
36-
if (process.env[key] !== undefined) continue;
37-
process.env[key] = rawValue.trim().replace(/^(['"])(.*)\1$/, '$2');
36+
const roots = [process.cwd(), resolve(process.cwd(), '..'), resolve(process.cwd(), '../..')];
37+
const seen = new Set<string>();
38+
39+
for (const root of roots) {
40+
for (const relativePath of ['.env.local', 'uapi/.env.local']) {
41+
const path = resolve(root, relativePath);
42+
if (seen.has(path)) continue;
43+
seen.add(path);
44+
if (!existsSync(path)) continue;
45+
const body = readFileSync(path, 'utf8');
46+
for (const line of body.split(/\r?\n/)) {
47+
const trimmed = line.trim();
48+
if (!trimmed || trimmed.startsWith('#')) continue;
49+
const match = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.*)$/.exec(trimmed);
50+
if (!match) continue;
51+
const [, key, rawValue] = match;
52+
if (process.env[key] !== undefined) continue;
53+
process.env[key] = rawValue.trim().replace(/^(['"])(.*)\1$/, '$2');
54+
}
3855
}
3956
}
4057
}
@@ -102,6 +119,48 @@ function sourceCredentials(): { username?: string; password?: string } {
102119
};
103120
}
104121

122+
function redactKnownSecrets(text: string): string {
123+
let redacted = text;
124+
for (const key of REDACTED_OUTPUT_ENV_KEYS) {
125+
const value = process.env[key];
126+
if (typeof value !== 'string' || value.length < 8) continue;
127+
redacted = redacted.split(value).join('[redacted]');
128+
}
129+
return redacted;
130+
}
131+
132+
function summarizeEvidence(evidence: unknown): Record<string, unknown> | null {
133+
if (!evidence || typeof evidence !== 'object' || Array.isArray(evidence)) return null;
134+
const record = evidence as Record<string, unknown>;
135+
const error = record.error && typeof record.error === 'object'
136+
? record.error as Record<string, unknown>
137+
: null;
138+
const events = Array.isArray(record.events) ? record.events : [];
139+
140+
return {
141+
schema: record.schema,
142+
harnessMode: record.harnessMode,
143+
resultState: record.resultState,
144+
resultReasons: Array.isArray(record.resultReasons)
145+
? record.resultReasons.map((reason) => redactKnownSecrets(String(reason)))
146+
: [],
147+
error: error
148+
? {
149+
name: error.name,
150+
message: redactKnownSecrets(String(error.message || '')),
151+
}
152+
: null,
153+
outputKeys: record.output && typeof record.output === 'object'
154+
? Object.keys(record.output as Record<string, unknown>)
155+
: [],
156+
eventTypes: events
157+
.map((event) => event && typeof event === 'object'
158+
? (event as Record<string, unknown>).type
159+
: null)
160+
.filter(Boolean),
161+
};
162+
}
163+
105164
async function main(): Promise<void> {
106165
loadLocalEnvFiles();
107166
requireHarnessOptIn();
@@ -166,8 +225,18 @@ async function main(): Promise<void> {
166225
label: command.label,
167226
exitCode: command.exitCode,
168227
})),
228+
commands: result.commands.map((command) => ({
229+
label: command.label,
230+
exitCode: command.exitCode,
231+
stdoutTail: redactKnownSecrets(command.stdout.slice(-1200)),
232+
stderrTail: redactKnownSecrets(command.stderr.slice(-1200)),
233+
})),
169234
evidencePresent: result.artifacts.evidence !== null,
170235
telemetryPresent: result.artifacts.telemetry !== null,
236+
evidence: summarizeEvidence(result.artifacts.evidence),
237+
telemetryLineCount: result.artifacts.telemetry
238+
? result.artifacts.telemetry.split(/\r?\n/).filter(Boolean).length
239+
: 0,
171240
},
172241
null,
173242
2

packages/pipeline-hosts/src/vercel-sandbox-host.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,7 @@ function withVercelAccessTokenAuth(createOptions: PipelineHarnessPlan['createOpt
184184
}
185185

186186
export async function loadVercelSandboxFactory(): Promise<SandboxFactory> {
187-
const dynamicImport = new Function('specifier', 'return import(specifier)') as (
188-
specifier: string
189-
) => Promise<{ Sandbox?: SandboxFactory }>;
190-
const module = await dynamicImport('@vercel/sandbox');
187+
const module = await import('@vercel/sandbox') as { Sandbox?: SandboxFactory };
191188
if (!module.Sandbox?.create) {
192189
throw new Error('@vercel/sandbox did not expose Sandbox.create().');
193190
}

uapi/next.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ let nextConfig = {
6161
experimental: {
6262
// Allow importing source files from outside the `uapi` package directory.
6363
externalDir: true,
64+
// Keep the Vercel Sandbox SDK as a traced Node runtime package. Bundling it
65+
// pulls in undici syntax newer than this Next/SWC build pipeline accepts.
66+
serverComponentsExternalPackages: ['@vercel/sandbox'],
6467
},
6568
// Transpile workspace packages that the Next app imports directly or
6669
// transitively so webpack/SWC can handle TS/ESM + monorepo paths.

0 commit comments

Comments
 (0)