@cat-factory/executor-harness 1.70.0 → 1.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/job.js CHANGED
@@ -309,8 +309,12 @@ export function parsePackageRegistries(value, env = process.env) {
309
309
  if (!allowed.has(host)) {
310
310
  throw new Error(`Invalid job: 'packageRegistries[${i}].host' '${host}' is not an allowed npm registry host`);
311
311
  }
312
- if (!Array.isArray(entry.scopes) || entry.scopes.length === 0) {
313
- throw new Error(`Invalid job: 'packageRegistries[${i}].scopes' must be a non-empty array`);
312
+ // An EMPTY scope list is valid and deliberate: the entry then only authenticates its
313
+ // host, leaving every package to resolve from the default registry unless a dependency
314
+ // pins this one itself. Mapping a scope is all-or-nothing, so a workspace mixing private
315
+ // and public packages under one scope must be able to skip it.
316
+ if (!Array.isArray(entry.scopes)) {
317
+ throw new Error(`Invalid job: 'packageRegistries[${i}].scopes' must be an array`);
314
318
  }
315
319
  const scopes = entry.scopes.map((scope, j) => {
316
320
  const s = str(scope, `packageRegistries[${i}].scopes[${j}]`).trim();
@@ -703,6 +707,8 @@ function assembleAgentJob(o, mode, agentField, parts) {
703
707
  */
704
708
  function collectOptionalRequestFields(o) {
705
709
  return {
710
+ ...(typeof o.workspaceId === 'string' && o.workspaceId ? { workspaceId: o.workspaceId } : {}),
711
+ ...(typeof o.executionId === 'string' && o.executionId ? { executionId: o.executionId } : {}),
706
712
  ...(typeof o.githubApiBase === 'string' ? { githubApiBase: o.githubApiBase } : {}),
707
713
  ...(typeof o.webToolsGuidance === 'string' ? { webToolsGuidance: o.webToolsGuidance } : {}),
708
714
  ...(o.webSearch === true ? { webSearch: true } : {}),
package/dist/server.js CHANGED
@@ -65,6 +65,11 @@ describe) {
65
65
  // image carries no runtime deps.
66
66
  const KINDS = {
67
67
  agent: defineKind(parseAgentJob, handleAgent, (job) => ({
68
+ // The backend's correlation ids first, so EVERY line this job emits joins to the run in the
69
+ // backend's own logs. Absent on a body from a backend older than the field — the rest of the
70
+ // description is unaffected.
71
+ ...(job.workspaceId ? { workspaceId: job.workspaceId } : {}),
72
+ ...(job.executionId ? { executionId: job.executionId } : {}),
68
73
  mode: job.mode,
69
74
  repo: `${job.repo.owner}/${job.repo.name}`,
70
75
  branch: job.branch,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/executor-harness",
3
- "version": "1.70.0",
3
+ "version": "1.74.0",
4
4
  "description": "Container payload: a thin TypeScript wrapper that runs the Pi coding agent against a cloned repo and opens a PR. Runs in the Cloudflare Container (and, in local native mode, as a host process); carries no secrets.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,9 +26,9 @@
26
26
  "hono": "^4.12.32",
27
27
  "typescript": "7.0.2",
28
28
  "vitest": "^4.1.10",
29
- "@cat-factory/kernel": "0.179.0",
30
- "@cat-factory/server": "0.167.0",
31
- "@cat-factory/spend": "0.12.108"
29
+ "@cat-factory/kernel": "0.185.1",
30
+ "@cat-factory/server": "0.172.2",
31
+ "@cat-factory/spend": "0.12.115"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsc -p tsconfig.json",
package/src/job.ts CHANGED
@@ -420,7 +420,7 @@ export interface PackageRegistrySpec {
420
420
  ecosystem: 'npm'
421
421
  /** Registry host, e.g. `registry.npmjs.org` — allowlisted, never a full URL. */
422
422
  host: string
423
- /** npm scopes (`@org`) routed to this registry. */
423
+ /** npm scopes (`@org`) routed to this registry; EMPTY ⇒ authenticate the host only. */
424
424
  scopes: string[]
425
425
  token: string
426
426
  }
@@ -468,8 +468,12 @@ export function parsePackageRegistries(
468
468
  `Invalid job: 'packageRegistries[${i}].host' '${host}' is not an allowed npm registry host`,
469
469
  )
470
470
  }
471
- if (!Array.isArray(entry.scopes) || entry.scopes.length === 0) {
472
- throw new Error(`Invalid job: 'packageRegistries[${i}].scopes' must be a non-empty array`)
471
+ // An EMPTY scope list is valid and deliberate: the entry then only authenticates its
472
+ // host, leaving every package to resolve from the default registry unless a dependency
473
+ // pins this one itself. Mapping a scope is all-or-nothing, so a workspace mixing private
474
+ // and public packages under one scope must be able to skip it.
475
+ if (!Array.isArray(entry.scopes)) {
476
+ throw new Error(`Invalid job: 'packageRegistries[${i}].scopes' must be an array`)
473
477
  }
474
478
  const scopes = entry.scopes.map((scope, j) => {
475
479
  const s = str(scope, `packageRegistries[${i}].scopes[${j}]`).trim()
@@ -703,6 +707,17 @@ export interface ValidationSpec {
703
707
 
704
708
  export interface AgentJob extends HarnessAuthFields {
705
709
  jobId: string
710
+ /**
711
+ * The backend run this job belongs to, bound onto the per-job logger beside `jobId` and used
712
+ * for NOTHING else. The container is the far side of the platform's longest seam: the backend
713
+ * knows a run as `executionId` and the harness knew it only as `jobId`, so a container log line
714
+ * could not be joined to the run that dispatched it except through the
715
+ * `${executionId}-${agentKind}` job-id naming convention. Optional because a body predating the
716
+ * field (or a hand-rolled acceptance fixture) must still run — an absent id costs correlation,
717
+ * never the job.
718
+ */
719
+ workspaceId?: string
720
+ executionId?: string
706
721
  mode: AgentMode
707
722
  systemPrompt: string
708
723
  userPrompt: string
@@ -1435,6 +1450,8 @@ function assembleAgentJob(
1435
1450
  */
1436
1451
  function collectOptionalRequestFields(o: Record<string, unknown>): Partial<AgentJob> {
1437
1452
  return {
1453
+ ...(typeof o.workspaceId === 'string' && o.workspaceId ? { workspaceId: o.workspaceId } : {}),
1454
+ ...(typeof o.executionId === 'string' && o.executionId ? { executionId: o.executionId } : {}),
1438
1455
  ...(typeof o.githubApiBase === 'string' ? { githubApiBase: o.githubApiBase } : {}),
1439
1456
  ...(typeof o.webToolsGuidance === 'string' ? { webToolsGuidance: o.webToolsGuidance } : {}),
1440
1457
  ...(o.webSearch === true ? { webSearch: true } : {}),
package/src/server.ts CHANGED
@@ -82,6 +82,11 @@ function defineKind<TJob extends { jobId: string }, TResult extends JobResultBas
82
82
  // image carries no runtime deps.
83
83
  const KINDS: Record<string, KindEntry> = {
84
84
  agent: defineKind(parseAgentJob, handleAgent, (job) => ({
85
+ // The backend's correlation ids first, so EVERY line this job emits joins to the run in the
86
+ // backend's own logs. Absent on a body from a backend older than the field — the rest of the
87
+ // description is unaffected.
88
+ ...(job.workspaceId ? { workspaceId: job.workspaceId } : {}),
89
+ ...(job.executionId ? { executionId: job.executionId } : {}),
85
90
  mode: job.mode,
86
91
  repo: `${job.repo.owner}/${job.repo.name}`,
87
92
  branch: job.branch,