@cat-factory/server 0.325.2 → 0.326.1

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.
Files changed (47) hide show
  1. package/dist/agents/CompositeAgentExecutor.d.ts +22 -3
  2. package/dist/agents/CompositeAgentExecutor.d.ts.map +1 -1
  3. package/dist/agents/CompositeAgentExecutor.js +91 -5
  4. package/dist/agents/CompositeAgentExecutor.js.map +1 -1
  5. package/dist/agents/DelegatedAgentExecutor.d.ts +147 -0
  6. package/dist/agents/DelegatedAgentExecutor.d.ts.map +1 -0
  7. package/dist/agents/DelegatedAgentExecutor.js +537 -0
  8. package/dist/agents/DelegatedAgentExecutor.js.map +1 -0
  9. package/dist/agents/brief.d.ts +61 -0
  10. package/dist/agents/brief.d.ts.map +1 -0
  11. package/dist/agents/brief.js +94 -0
  12. package/dist/agents/brief.js.map +1 -0
  13. package/dist/agents/containerJobAddressing.d.ts +1 -20
  14. package/dist/agents/containerJobAddressing.d.ts.map +1 -1
  15. package/dist/agents/containerJobAddressing.js +5 -23
  16. package/dist/agents/containerJobAddressing.js.map +1 -1
  17. package/dist/agents/delegatedExecutorHost.d.ts +90 -0
  18. package/dist/agents/delegatedExecutorHost.d.ts.map +1 -0
  19. package/dist/agents/delegatedExecutorHost.js +124 -0
  20. package/dist/agents/delegatedExecutorHost.js.map +1 -0
  21. package/dist/agents/delegationCredentials.d.ts +20 -0
  22. package/dist/agents/delegationCredentials.d.ts.map +1 -0
  23. package/dist/agents/delegationCredentials.js +63 -0
  24. package/dist/agents/delegationCredentials.js.map +1 -0
  25. package/dist/agents/delegationWorkBranch.d.ts +31 -0
  26. package/dist/agents/delegationWorkBranch.d.ts.map +1 -0
  27. package/dist/agents/delegationWorkBranch.js +103 -0
  28. package/dist/agents/delegationWorkBranch.js.map +1 -0
  29. package/dist/agents/jobBody.d.ts.map +1 -1
  30. package/dist/agents/jobBody.js +8 -9
  31. package/dist/agents/jobBody.js.map +1 -1
  32. package/dist/index.d.ts +3 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +7 -0
  35. package/dist/index.js.map +1 -1
  36. package/dist/modules/publicApi/openapiDocument.generated.d.ts +1 -1
  37. package/dist/modules/publicApi/openapiDocument.generated.d.ts.map +1 -1
  38. package/dist/modules/publicApi/openapiDocument.generated.js +1 -1
  39. package/dist/modules/publicApi/openapiDocument.generated.js.map +1 -1
  40. package/dist/modules/workspaces/WorkspaceController.d.ts.map +1 -1
  41. package/dist/modules/workspaces/WorkspaceController.js +42 -1
  42. package/dist/modules/workspaces/WorkspaceController.js.map +1 -1
  43. package/dist/persistence/agentKinds.d.ts +6 -0
  44. package/dist/persistence/agentKinds.d.ts.map +1 -1
  45. package/dist/persistence/agentKinds.js +6 -0
  46. package/dist/persistence/agentKinds.js.map +1 -1
  47. package/package.json +13 -13
@@ -0,0 +1,94 @@
1
+ import { bugFixGuidanceFor, composeBlockSystemPrompt, standardsDeliveredAsFiles, standardsVerbosityFor, userPromptFor, } from '@cat-factory/agents';
2
+ import { dispatchSystemPromptFor } from './promptOverrides.js';
3
+ /**
4
+ * Compose the executor-independent half of a dispatch's instructions.
5
+ *
6
+ * `dispatch` carries the resolved checkout facts for the kinds whose own prompt names a branch.
7
+ * Both callers have one (a delegated executor gets a checkout too: its own), which is the
8
+ * difference between this and an inline caller, and it is why the parameter is required here
9
+ * rather than optional as it is on `userPromptFor`.
10
+ */
11
+ export function composeAgentBriefCore(context, registry, dispatch) {
12
+ return {
13
+ roleSystemPrompt: composeRoleSystemPrompt(context, registry),
14
+ // `materialized: true` on BOTH paths: linked context renders as an index pointing at the
15
+ // `.cat-context/` files rather than folding their bodies into the prompt, because the files
16
+ // themselves travel beside it. A delegated executor that cannot materialise them has to fold
17
+ // the bodies into its own input instead: the brief carries both halves so either is possible,
18
+ // and rendering the index for one caller and the bodies for the other would mean the two
19
+ // executors were briefed differently on the same task.
20
+ userPrompt: userPromptFor(context, registry, { materialized: true, dispatch }),
21
+ contextFiles: [...(context.injectedContextFiles ?? [])],
22
+ };
23
+ }
24
+ /**
25
+ * The role prompt for one dispatch: the workspace's override (or the shipped track prompt), the
26
+ * block's resolved standards at this kind's verbosity, and its trait guidance.
27
+ *
28
+ * Its own exported function because two dispatch paths must produce the identical string from one
29
+ * context. It is the piece a workspace actually EDITS, so a drift here is the failure that matters
30
+ * most: the standards a team agreed on reaching one executor and not the other, with nothing
31
+ * anywhere reporting it.
32
+ */
33
+ export function composeRoleSystemPrompt(context, registry) {
34
+ return composeBlockSystemPrompt(dispatchSystemPromptFor(context, registry), context.block, registry.standardsDelivery(context.agentKind), standardsDeliveredAsFiles(context.injectedContextFiles), standardsVerbosityFor(context.agentKind, registry));
35
+ }
36
+ /**
37
+ * Build the {@link DelegationBrief} for one delegated dispatch.
38
+ *
39
+ * `ownService` is FILLED IN rather than forwarded optionally: the brief's contract is a
40
+ * discriminated result, and an omitted one reads to a model exactly like a task whose product is
41
+ * obvious. A context that carries none genuinely does not know which system the work belongs to,
42
+ * and `not-under-a-service` is what says so.
43
+ */
44
+ export function composeDelegationBrief(context, registry, args) {
45
+ const dispatch = {
46
+ baseBranch: args.target.branches.base,
47
+ // The executor checks out the work branch: a delegated step is a producer, and the branch every
48
+ // step of this run's pipeline shares is the one its change has to land on.
49
+ checkoutBranch: args.target.branches.work,
50
+ workBranch: args.target.branches.work,
51
+ // One repo. A delegated executor is handed one repository and one branch pair; the multi-repo
52
+ // fan-out is a property of OUR harness's sibling checkouts, and claiming it here would have a
53
+ // kind's prompt describe checkouts the external system never makes.
54
+ multiRepo: false,
55
+ };
56
+ const core = composeAgentBriefCore(context, registry, dispatch);
57
+ // Bug-fix guidance rides the brief for the same reason it rides a container coding dispatch: it
58
+ // is about the WORK (fix the reported defect, do not merely satisfy the reproduction test), not
59
+ // about the machine. Empty for every kind and every run that no `repro-test` preceded.
60
+ const bugFix = bugFixGuidanceFor(context);
61
+ return {
62
+ correlationKey: args.correlationKey,
63
+ workspaceId: args.workspaceId,
64
+ // The same value `task.id` carries, stated twice because the two are different contracts: this
65
+ // one is the LOOKUP KEY `deps.repoFiles` takes beside the workspace, and `task` is the work's
66
+ // identity as an executor records it. An executor keying its repository read off the identity
67
+ // is one rename away from a resolver that answers nothing.
68
+ blockId: args.blockId,
69
+ runId: args.runId,
70
+ stepIndex: args.stepIndex,
71
+ agentKind: context.agentKind,
72
+ task: {
73
+ id: args.blockId,
74
+ title: context.block.title,
75
+ description: context.block.description,
76
+ ...(args.target.trackerRef ? { trackerRef: args.target.trackerRef } : {}),
77
+ },
78
+ repo: args.target.repo,
79
+ branches: args.target.branches,
80
+ systemPrompt: bugFix ? `${core.roleSystemPrompt}\n\n${bugFix}` : core.roleSystemPrompt,
81
+ userPrompt: core.userPrompt,
82
+ contextFiles: core.contextFiles,
83
+ ownService: ownServiceFor(context),
84
+ };
85
+ }
86
+ /** The discriminated own-service answer, defaulting to the one that SAYS the platform does not know. */
87
+ function ownServiceFor(context) {
88
+ return context.ownService ?? { stated: false, reason: 'not-under-a-service' };
89
+ }
90
+ /** Narrow a repo projection's stored provider to the union, defaulting to the pre-column value. */
91
+ export function briefRepoProvider(provider) {
92
+ return provider ?? 'github';
93
+ }
94
+ //# sourceMappingURL=brief.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brief.js","sourceRoot":"","sources":["../../src/agents/brief.ts"],"names":[],"mappings":"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"}
@@ -1,25 +1,6 @@
1
1
  import type { AgentJobHandle, RunnerImageVariant, RunnerJobRef } from '@cat-factory/kernel';
2
2
  import type { AgentKindRegistry } from '@cat-factory/agents';
3
- /**
4
- * The harness job id for one dispatch: the run (execution) id, the agent kind, and — past the
5
- * first job of that kind in the run — the `dispatchEpoch`. A run executes a sequence of steps that
6
- * all share the one per-run container, so each job needs an id that is UNIQUE WITHIN THE RUN: the
7
- * harness keys its per-kind job registries by it, and two jobs sharing an id alias there (the bug
8
- * where an `architect` /explore poll read back the `spec-writer`'s /spec result). The run itself is
9
- * addressed separately by the execution id (the {@link RunnerJobRef.runId}).
10
- *
11
- * The epoch is what makes that uniqueness total, because the engine dispatches one kind more than
12
- * once per run in two ways: a step RE-dispatched (a Tester re-test after a fixer round, a gate's
13
- * helper retry, a companion's rework round, an eviction recovery) and the same helper kind
14
- * escalated off DIFFERENT steps (`fixer` serves four gates). The harness re-attaches to an EXISTING
15
- * job id rather than re-running (replay idempotency), and a container-reusing transport — a warm
16
- * local pool, a self-hosted runner pool — keeps that registry alive across rounds, since reclaiming
17
- * a pooled member does NOT destroy it. So a reused id replays a completed job: the Tester that
18
- * appeared to "pass regardless" and never re-tested, an eviction recovery landing back on the job
19
- * whose runner just died. `dispatchEpochFor` counts the run's prior dispatches of the kind, so the
20
- * id names the n-th job of that kind and the run's first keeps the unsuffixed shape.
21
- */
22
- export declare function stepJobId(executionId: string, agentKind: string, dispatchEpoch?: number): string;
3
+ export { stepJobId } from '@cat-factory/kernel';
23
4
  /** The provider slug from a handle's `provider:model` string (fallback when the handle omits `provider`). */
24
5
  export declare function providerOf(model: string | undefined): string;
25
6
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"containerJobAddressing.d.ts","sourceRoot":"","sources":["../../src/agents/containerJobAddressing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAW5D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,SAAI,GAAG,MAAM,CAG3F;AAED,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,iBAAiB,GAC1B,kBAAkB,GAAG,SAAS,CAEhC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,QAAQ,EAAE,iBAAiB,GAC1B,CAAC,kBAAkB,GAAG,SAAS,CAAC,EAAE,CAOpC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,YAAY,CAO9F"}
1
+ {"version":3,"file":"containerJobAddressing.d.ts","sourceRoot":"","sources":["../../src/agents/containerJobAddressing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAe5D,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE/C,6GAA6G;AAC7G,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAI5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,iBAAiB,GAC1B,kBAAkB,GAAG,SAAS,CAEhC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,QAAQ,EAAE,iBAAiB,GAC1B,CAAC,kBAAkB,GAAG,SAAS,CAAC,EAAE,CAOpC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,iBAAiB,GAAG,YAAY,CAO9F"}
@@ -6,29 +6,11 @@
6
6
  // context or the job handle, which is what lets the dispatch site and the poll site derive the
7
7
  // same answer with nothing carried between them: the poll runs in another process after a
8
8
  // durable replay and rebuilds the handle from the persisted step alone.
9
- /**
10
- * The harness job id for one dispatch: the run (execution) id, the agent kind, and — past the
11
- * first job of that kind in the run — the `dispatchEpoch`. A run executes a sequence of steps that
12
- * all share the one per-run container, so each job needs an id that is UNIQUE WITHIN THE RUN: the
13
- * harness keys its per-kind job registries by it, and two jobs sharing an id alias there (the bug
14
- * where an `architect` /explore poll read back the `spec-writer`'s /spec result). The run itself is
15
- * addressed separately by the execution id (the {@link RunnerJobRef.runId}).
16
- *
17
- * The epoch is what makes that uniqueness total, because the engine dispatches one kind more than
18
- * once per run in two ways: a step RE-dispatched (a Tester re-test after a fixer round, a gate's
19
- * helper retry, a companion's rework round, an eviction recovery) and the same helper kind
20
- * escalated off DIFFERENT steps (`fixer` serves four gates). The harness re-attaches to an EXISTING
21
- * job id rather than re-running (replay idempotency), and a container-reusing transport — a warm
22
- * local pool, a self-hosted runner pool — keeps that registry alive across rounds, since reclaiming
23
- * a pooled member does NOT destroy it. So a reused id replays a completed job: the Tester that
24
- * appeared to "pass regardless" and never re-tested, an eviction recovery landing back on the job
25
- * whose runner just died. `dispatchEpochFor` counts the run's prior dispatches of the kind, so the
26
- * id names the n-th job of that kind and the run's first keeps the unsuffixed shape.
27
- */
28
- export function stepJobId(executionId, agentKind, dispatchEpoch = 0) {
29
- const base = `${executionId}-${agentKind}`;
30
- return dispatchEpoch > 0 ? `${base}-${dispatchEpoch}` : base;
31
- }
9
+ // The per-dispatch job id itself lives in KERNEL (`stepJobId`), because the engine now mints the
10
+ // same string as a delegated step's correlation key and had to be able to commit it before any
11
+ // executor is called. Re-exported here so every existing importer keeps resolving it from the
12
+ // module that addresses container jobs.
13
+ export { stepJobId } from '@cat-factory/kernel';
32
14
  /** The provider slug from a handle's `provider:model` string (fallback when the handle omits `provider`). */
33
15
  export function providerOf(model) {
34
16
  if (!model)
@@ -1 +1 @@
1
- {"version":3,"file":"containerJobAddressing.js","sourceRoot":"","sources":["../../src/agents/containerJobAddressing.ts"],"names":[],"mappings":"AAGA,8FAA8F;AAC9F,wFAAwF;AACxF,EAAE;AACF,gGAAgG;AAChG,+FAA+F;AAC/F,+FAA+F;AAC/F,0FAA0F;AAC1F,wEAAwE;AAExE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS,CAAC,WAAmB,EAAE,SAAiB,EAAE,aAAa,GAAG,CAAC;IACjF,MAAM,IAAI,GAAG,GAAG,WAAW,IAAI,SAAS,EAAE,CAAA;IAC1C,OAAO,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;AAC9D,CAAC;AAED,6GAA6G;AAC7G,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAChC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,SAA6B,EAC7B,QAA2B;IAE3B,OAAO,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACrE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA6B,EAC7B,QAA2B;IAE3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAiC,CAAC,SAAS,CAAC,CAAC,CAAA;IACrE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,IAAI,KAAK,IAAI,KAAK,KAAK,SAAS;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB,EAAE,QAA2B;IAC9E,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACzD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;QACnC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5B,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"containerJobAddressing.js","sourceRoot":"","sources":["../../src/agents/containerJobAddressing.ts"],"names":[],"mappings":"AAGA,8FAA8F;AAC9F,wFAAwF;AACxF,EAAE;AACF,gGAAgG;AAChG,+FAA+F;AAC/F,+FAA+F;AAC/F,0FAA0F;AAC1F,wEAAwE;AAExE,iGAAiG;AACjG,+FAA+F;AAC/F,8FAA8F;AAC9F,wCAAwC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAE/C,6GAA6G;AAC7G,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAChC,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AAClD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,SAA6B,EAC7B,QAA2B;IAE3B,OAAO,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AACrE,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAA6B,EAC7B,QAA2B;IAE3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAiC,CAAC,SAAS,CAAC,CAAC,CAAA;IACrE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC7C,IAAI,KAAK,IAAI,KAAK,KAAK,SAAS;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IACvD,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAA;AACtB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAsB,EAAE,QAA2B;IAC9E,MAAM,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;IACzD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;QACnC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5B,CAAA;AACH,CAAC"}
@@ -0,0 +1,90 @@
1
+ import type { AgentContextRecorder, Clock, DelegatedExecutorRegistry, DelegatedFetch, Logger, ResolveRunRepoContext, TaskRepository, ToolSecretResolver, UrlSafetyPolicy } from '@cat-factory/kernel';
2
+ import type { AgentKindRegistry } from '@cat-factory/agents';
3
+ import { DelegatedAgentExecutor } from './DelegatedAgentExecutor.js';
4
+ import type { ResolveRepoOrigin, ResolveRepoTarget } from './repoTargeting.js';
5
+ /** What a facade supplies to stand the delegated arm up. */
6
+ export interface DelegatedExecutorHostOptions {
7
+ delegatedExecutorRegistry: DelegatedExecutorRegistry;
8
+ agentKindRegistry: AgentKindRegistry;
9
+ resolveRepoTarget: ResolveRepoTarget;
10
+ /**
11
+ * Where a repo is reached: the clone URL plus the VCS provider.
12
+ *
13
+ * REQUIRED, for the reason `urlSafetyPolicy` below is: optional, the Worker facade simply did
14
+ * not pass it, and every delegated brief on that facade named a `https://github.com/...` clone
15
+ * URL, including for repositories on the deployment's own GitLab. A facade with nothing to
16
+ * resolve passes `githubRepoOrigin` by name.
17
+ */
18
+ resolveRepoOrigin: ResolveRepoOrigin;
19
+ /**
20
+ * The engine's checkout-free repo binding, re-used as the `repoFiles` an executor may stage its
21
+ * own context layer through, and as what the engine creates a `platform-creates` work branch
22
+ * with. The SAME seam a registered kind's pre/post-ops run over, rather than a second binding:
23
+ * an executor writing `.cat-context/` onto the work branch and a preOp writing it are the same
24
+ * operation, and two bindings would be two caches and two head memos.
25
+ *
26
+ * Absent ⇒ this deployment configured no VCS provider. NOT asserted here even though a
27
+ * `platform-creates` registration needs it: this function runs inside the Worker's per-request
28
+ * container build, so a throw turns a delegated-only misconfiguration into a 500 on every
29
+ * unrelated endpoint, including the settings the operator would go and look at. The dispatch
30
+ * that actually needs the branch refuses instead, by name and with translated copy
31
+ * (`delegated_work_branch_unprepared`).
32
+ */
33
+ resolveRunRepoContext?: ResolveRunRepoContext;
34
+ taskRepository?: TaskRepository;
35
+ resolveToolSecrets?: ToolSecretResolver;
36
+ agentContextObservability?: AgentContextRecorder;
37
+ /**
38
+ * The deployment's outbound-URL policy. The SAME one the notification-webhook sender is held to,
39
+ * because an executor is an outbound HTTP surface the deployment configured and a second set of
40
+ * SSRF rules is a set nobody maintains.
41
+ *
42
+ * REQUIRED, though its value may be `undefined` (which means the strict public-https default).
43
+ * Optional, it was declared here, declared on the kernel port, documented on both, and passed by
44
+ * neither facade: every registered executor was built with an SSRF control that existed only in
45
+ * the types. A required field makes forgetting it a typecheck failure instead.
46
+ *
47
+ * It is ENFORCED here rather than handed to each executor, by wrapping the fetch they are all
48
+ * built over (see {@link policyCheckedFetch}). An executor is deployment-authored code, and a
49
+ * control every author has to remember to apply is the same control that existed only in the
50
+ * types.
51
+ */
52
+ urlSafetyPolicy: UrlSafetyPolicy | undefined;
53
+ /** The runtime's fetch. Defaults to the global one, which both runtimes provide. */
54
+ fetchImpl?: DelegatedFetch;
55
+ logger: Logger;
56
+ clock: Clock;
57
+ }
58
+ /**
59
+ * Build the delegated arm for the composite executor.
60
+ *
61
+ * Built UNCONDITIONALLY, even when the registry is empty. The alternative (a null arm when
62
+ * nothing is registered) reads as an optimisation and is a trap: a MOTHERSHIP-MODE node resolves
63
+ * its agent kinds from the mothership and its own registry can be a build behind, so "this process
64
+ * registers none" is not the same fact as "this run has no delegated step". With the arm always
65
+ * present, such a step is refused by name (`delegated_executor_unwired`, naming what IS
66
+ * registered) instead of a bare "no delegated executor wired" from the composite.
67
+ */
68
+ export declare function buildDelegatedAgentExecutor(options: DelegatedExecutorHostOptions): DelegatedAgentExecutor;
69
+ /**
70
+ * How long one executor call may take, redirects included, before it is abandoned.
71
+ *
72
+ * A delegated executor's own calls are API requests (dispatch, poll, cancel), not the external
73
+ * WORK, whose hours are bounded by the poll policy instead. Without a deadline a hung endpoint
74
+ * holds `DelegatedAgentExecutor.pollJob` open for ever, which on Node ties up a pg-boss worker and
75
+ * on the Worker burns the invocation: the ordinary shape of an outage, and the reason the
76
+ * notification-webhook sender sets one against this same `safeFetch`. A caller that passes its own
77
+ * `signal` keeps it.
78
+ */
79
+ export declare const DELEGATED_REQUEST_TIMEOUT_MS = 30000;
80
+ /**
81
+ * How much of one response body an executor may read.
82
+ *
83
+ * The third protection `safe-fetch` exists for, and the one a wrapper that only re-validates hops
84
+ * leaves off. An executor is deployment-authored code reading a system's JSON, and a broken or
85
+ * hostile endpoint answering hundreds of megabytes would otherwise be buffered whole in the
86
+ * isolate. Generous against the real payloads (a page of Actions runs is a few hundred KB) and
87
+ * fatal well before an OOM.
88
+ */
89
+ export declare const DELEGATED_RESPONSE_MAX_BYTES: number;
90
+ //# sourceMappingURL=delegatedExecutorHost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegatedExecutorHost.d.ts","sourceRoot":"","sources":["../../src/agents/delegatedExecutorHost.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,KAAK,EAEL,yBAAyB,EACzB,cAAc,EAEd,MAAM,EACN,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,eAAe,EAChB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAQ5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAW9E,4DAA4D;AAC5D,MAAM,WAAW,4BAA4B;IAC3C,yBAAyB,EAAE,yBAAyB,CAAA;IACpD,iBAAiB,EAAE,iBAAiB,CAAA;IACpC,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;;;;;OAOG;IACH,iBAAiB,EAAE,iBAAiB,CAAA;IACpC;;;;;;;;;;;;;OAaG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;IAC7C,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,yBAAyB,CAAC,EAAE,oBAAoB,CAAA;IAChD;;;;;;;;;;;;;;OAcG;IACH,eAAe,EAAE,eAAe,GAAG,SAAS,CAAA;IAC5C,oFAAoF;IACpF,SAAS,CAAC,EAAE,cAAc,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;CACb;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,sBAAsB,CA4BxB;AAYD;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,QAAkB,CAAA"}
@@ -0,0 +1,124 @@
1
+ import { UnavailableError } from '@cat-factory/kernel';
2
+ import { assertSafePublicUrl, readCappedText, safeFetch, DEFAULT_MAX_REDIRECTS, } from '@cat-factory/integrations';
3
+ import { DelegatedAgentExecutor } from './DelegatedAgentExecutor.js';
4
+ /**
5
+ * Build the delegated arm for the composite executor.
6
+ *
7
+ * Built UNCONDITIONALLY, even when the registry is empty. The alternative (a null arm when
8
+ * nothing is registered) reads as an optimisation and is a trap: a MOTHERSHIP-MODE node resolves
9
+ * its agent kinds from the mothership and its own registry can be a build behind, so "this process
10
+ * registers none" is not the same fact as "this run has no delegated step". With the arm always
11
+ * present, such a step is refused by name (`delegated_executor_unwired`, naming what IS
12
+ * registered) instead of a bare "no delegated executor wired" from the composite.
13
+ */
14
+ export function buildDelegatedAgentExecutor(options) {
15
+ const resolveRepoFiles = options.resolveRunRepoContext
16
+ ? repoFilesResolver(options.resolveRunRepoContext)
17
+ : undefined;
18
+ const executorDeps = {
19
+ logger: options.logger.child({ component: 'delegatedExecutor' }),
20
+ clock: options.clock,
21
+ fetchImpl: policyCheckedFetch(options.fetchImpl ?? globalThis.fetch, options.urlSafetyPolicy),
22
+ ...(resolveRepoFiles ? { repoFiles: resolveRepoFiles } : {}),
23
+ };
24
+ return new DelegatedAgentExecutor({
25
+ ...(resolveRepoFiles ? { resolveRepoFiles } : {}),
26
+ delegatedExecutorRegistry: options.delegatedExecutorRegistry,
27
+ agentKindRegistry: options.agentKindRegistry,
28
+ resolveRepoTarget: options.resolveRepoTarget,
29
+ resolveRepoOrigin: options.resolveRepoOrigin,
30
+ ...(options.taskRepository ? { taskRepository: options.taskRepository } : {}),
31
+ ...(options.resolveToolSecrets ? { resolveToolSecrets: options.resolveToolSecrets } : {}),
32
+ ...(options.agentContextObservability
33
+ ? { agentContextObservability: options.agentContextObservability }
34
+ : {}),
35
+ executorDeps,
36
+ logger: options.logger,
37
+ clock: options.clock,
38
+ });
39
+ }
40
+ /** Project the engine's run-repo binding down to the narrower shape an executor is handed. */
41
+ function repoFilesResolver(resolveRunRepoContext) {
42
+ return async ({ workspaceId, blockId }) => {
43
+ const bound = await resolveRunRepoContext(workspaceId, blockId);
44
+ return bound?.repo ?? null;
45
+ };
46
+ }
47
+ /**
48
+ * How long one executor call may take, redirects included, before it is abandoned.
49
+ *
50
+ * A delegated executor's own calls are API requests (dispatch, poll, cancel), not the external
51
+ * WORK, whose hours are bounded by the poll policy instead. Without a deadline a hung endpoint
52
+ * holds `DelegatedAgentExecutor.pollJob` open for ever, which on Node ties up a pg-boss worker and
53
+ * on the Worker burns the invocation: the ordinary shape of an outage, and the reason the
54
+ * notification-webhook sender sets one against this same `safeFetch`. A caller that passes its own
55
+ * `signal` keeps it.
56
+ */
57
+ export const DELEGATED_REQUEST_TIMEOUT_MS = 30_000;
58
+ /**
59
+ * How much of one response body an executor may read.
60
+ *
61
+ * The third protection `safe-fetch` exists for, and the one a wrapper that only re-validates hops
62
+ * leaves off. An executor is deployment-authored code reading a system's JSON, and a broken or
63
+ * hostile endpoint answering hundreds of megabytes would otherwise be buffered whole in the
64
+ * isolate. Generous against the real payloads (a page of Actions runs is a few hundred KB) and
65
+ * fatal well before an OOM.
66
+ */
67
+ export const DELEGATED_RESPONSE_MAX_BYTES = 4 * 1024 * 1024;
68
+ /**
69
+ * The fetch every registered executor is built over: the runtime's own, held to the deployment's
70
+ * outbound-URL policy on the first URL AND on every redirect hop, given a deadline, and capped on
71
+ * the way back.
72
+ *
73
+ * The SAME guard the notification-webhook sender uses, through the same `safeFetch`, because an
74
+ * executor is the same kind of surface: an operator-supplied base URL, a credential-bearing
75
+ * request, and a receiver free to answer 302. Re-validating each hop is the part a plain
76
+ * scheme check at registration cannot do, and `safeFetch` additionally strips the body and the
77
+ * credential headers when a hop crosses origins.
78
+ *
79
+ * All THREE of that module's protections are applied here rather than two, and for the reason the
80
+ * policy itself is enforced here: an executor is deployment-authored code, and a control every
81
+ * author has to remember to apply is a control that exists only in the types.
82
+ *
83
+ * A refused URL throws `ValidationError` out of the executor's own call, which its error path
84
+ * reports like any other refusal from its system.
85
+ */
86
+ function policyCheckedFetch(fetchImpl, policy) {
87
+ const assertSafe = (url) => assertSafePublicUrl(url, {
88
+ subject: 'Delegated executor',
89
+ label: 'endpoint',
90
+ ...(policy ? { policy } : {}),
91
+ });
92
+ const makeError = (status, message) => new UnavailableError(`A delegated executor's request could not be completed: ${message}`, 'delegated_executor_failed', { status });
93
+ return async (url, init) => {
94
+ const response = await safeFetch(url, {
95
+ ...(init ?? {}),
96
+ // The caller's own deadline wins where it set one; otherwise the deployment's. The port
97
+ // declares `signal` as `unknown` (kernel compiles against no runtime's globals), so the
98
+ // narrowing happens here, where a real `RequestInit` is being built.
99
+ signal: init?.signal ??
100
+ AbortSignal.timeout(DELEGATED_REQUEST_TIMEOUT_MS),
101
+ }, assertSafe, makeError, DEFAULT_MAX_REDIRECTS, fetchImpl);
102
+ return cappedResponse(response, makeError);
103
+ };
104
+ }
105
+ /**
106
+ * The response an executor sees: the real one's status and headers, with the two body readers
107
+ * routed through the running byte cap.
108
+ *
109
+ * A wrapper rather than a rule each executor applies, because the port hands them only `text()`
110
+ * and `json()` and neither can be bounded from the outside. The cap THROWS rather than truncating,
111
+ * which is right on this path: a body that overran is not a smaller body, and a JSON reader handed
112
+ * a prefix would parse a fault as a malformed payload.
113
+ */
114
+ function cappedResponse(response, makeError) {
115
+ const read = () => readCappedText(response, DELEGATED_RESPONSE_MAX_BYTES, makeError);
116
+ return {
117
+ ok: response.ok,
118
+ status: response.status,
119
+ headers: response.headers,
120
+ text: read,
121
+ json: async () => JSON.parse(await read()),
122
+ };
123
+ }
124
+ //# sourceMappingURL=delegatedExecutorHost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegatedExecutorHost.js","sourceRoot":"","sources":["../../src/agents/delegatedExecutorHost.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { DelegatedExecutorDefinition, Logger, ToolSecretResolver } from '@cat-factory/kernel';
2
+ export interface ResolveDelegationCredentialsInput {
3
+ definition: DelegatedExecutorDefinition;
4
+ workspaceId: string;
5
+ /** The block, so a per-service credential store can scope its lookup. */
6
+ blockId?: string;
7
+ /** Facade-wired; absent ⇒ an empty bag, which the executor's own system reports as a refusal. */
8
+ resolveToolSecrets?: ToolSecretResolver;
9
+ logger?: Logger;
10
+ }
11
+ /**
12
+ * Resolve every credential a registered executor declared, keyed by the name the executor reads.
13
+ *
14
+ * Never throws and never fails a dispatch. An unresolvable key simply is not in the bag, and the
15
+ * executor's own call then fails against its own system with that system's own message, which is
16
+ * strictly more useful than a platform-side refusal that names a key the operator has to go and
17
+ * map back to a vendor. The WARN below is what makes the gap visible either way.
18
+ */
19
+ export declare function resolveDelegationCredentials(input: ResolveDelegationCredentialsInput): Promise<Record<string, string>>;
20
+ //# sourceMappingURL=delegationCredentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegationCredentials.d.ts","sourceRoot":"","sources":["../../src/agents/delegationCredentials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAiClG,MAAM,WAAW,iCAAiC;IAChD,UAAU,EAAE,2BAA2B,CAAA;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iGAAiG;IACjG,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,iCAAiC,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAsDjC"}
@@ -0,0 +1,63 @@
1
+ import { noopLogger, runBestEffort } from '@cat-factory/kernel';
2
+ import { credentialInjectionName, isReservedPlatformEnvKey, reservedEnvKeyMessage, } from '@cat-factory/contracts';
3
+ /**
4
+ * Resolve every credential a registered executor declared, keyed by the name the executor reads.
5
+ *
6
+ * Never throws and never fails a dispatch. An unresolvable key simply is not in the bag, and the
7
+ * executor's own call then fails against its own system with that system's own message, which is
8
+ * strictly more useful than a platform-side refusal that names a key the operator has to go and
9
+ * map back to a vendor. The WARN below is what makes the gap visible either way.
10
+ */
11
+ export async function resolveDelegationCredentials(input) {
12
+ const declared = input.definition.credentials ?? [];
13
+ const resolver = input.resolveToolSecrets;
14
+ if (declared.length === 0 || !resolver)
15
+ return {};
16
+ const log = input.logger ?? noopLogger;
17
+ const admissible = declared.filter((credential) => {
18
+ if (!isReservedPlatformEnvKey(credential.key))
19
+ return true;
20
+ // WARN rather than the `debug` an optional missing key gets: this is never a deployment's
21
+ // stated normal, and its fix is a declaration rather than a variable to set.
22
+ log.warn('delegated executor declares a reserved credential key; withholding it', {
23
+ executor: input.definition.id,
24
+ credentialKey: credential.key,
25
+ detail: reservedEnvKeyMessage(credential.key),
26
+ });
27
+ return false;
28
+ });
29
+ if (admissible.length === 0)
30
+ return {};
31
+ const resolved = await runBestEffort(log, 'resolve delegated executor credentials', () => resolver.resolve({
32
+ workspaceId: input.workspaceId,
33
+ ...(input.blockId ? { blockId: input.blockId } : {}),
34
+ subject: { kind: 'delegated-executor', id: input.definition.id },
35
+ // Distinct LOOKUP keys: one stored value delivered under two names is an allowed
36
+ // declaration, and asking for the same key twice in one call is not.
37
+ keys: [...new Set(admissible.map((credential) => credential.key))].map((key) => ({ key })),
38
+ }), { executor: input.definition.id });
39
+ const bag = {};
40
+ for (const credential of admissible) {
41
+ const value = resolved?.[credential.key];
42
+ // The bag is keyed by the name the EXECUTOR reads (`envName` when it declared one, else the
43
+ // lookup key), the same fallback every other capability applies, so a declaration that renames
44
+ // a key for a vendor's SDK reads the same here as it does in a container.
45
+ if (value) {
46
+ bag[credentialInjectionName(credential)] = value;
47
+ continue;
48
+ }
49
+ if (credential.required === false) {
50
+ log.debug('optional delegated credential did not resolve; calling without it', {
51
+ executor: input.definition.id,
52
+ credentialKey: credential.key,
53
+ });
54
+ continue;
55
+ }
56
+ log.warn('delegated credential did not resolve; the executor is called without it', {
57
+ executor: input.definition.id,
58
+ credentialKey: credential.key,
59
+ });
60
+ }
61
+ return bag;
62
+ }
63
+ //# sourceMappingURL=delegationCredentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegationCredentials.js","sourceRoot":"","sources":["../../src/agents/delegationCredentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { type DelegatedRepoFilesResolver, type DelegationBrief, type Logger } from '@cat-factory/kernel';
2
+ /** What {@link ensureDelegatedWorkBranch} needs, as bound callbacks rather than a container. */
3
+ export interface DelegationWorkBranchDeps {
4
+ /** Absent ⇒ this deployment configured no VCS provider, which is its own refusal below. */
5
+ resolveRepoFiles: DelegatedRepoFilesResolver | undefined;
6
+ logger: Logger;
7
+ }
8
+ /** One request to prepare a branch: whose dispatch it is for, and who may create it. */
9
+ export interface DelegationWorkBranchRequest {
10
+ executorId: string;
11
+ /**
12
+ * Whether the task named this branch itself (an apriori WORKING branch) rather than the engine
13
+ * deriving `cat-factory/<blockId>`. The platform never CREATES one of those: a run whose user
14
+ * picked an existing feature branch and found an empty one instead cannot tell that from the
15
+ * run ignoring their choice. Probed and refused when absent, exactly as the container path
16
+ * refuses it (`ContainerAgentExecutor.resolveWorkBranchReady`).
17
+ */
18
+ apriori: boolean;
19
+ }
20
+ /**
21
+ * Make sure the brief's work branch exists before the executor is called, creating it at the base
22
+ * branch's head when the platform is the one allowed to.
23
+ *
24
+ * IDEMPOTENT by construction, which both durable drivers require: a replayed dispatch re-reads the
25
+ * head and finds the ref it made last time. The concurrent case is settled by CONTENT rather than
26
+ * by parsing a provider's refusal, because the two providers answer a lost create differently (a
27
+ * GitHub 422, a GitLab 400) and a failing create is indistinguishable from a losing one by status
28
+ * alone: re-read the ref, and let the write's own error propagate when it genuinely is not there.
29
+ */
30
+ export declare function ensureDelegatedWorkBranch(deps: DelegationWorkBranchDeps, brief: DelegationBrief, request: DelegationWorkBranchRequest): Promise<void>;
31
+ //# sourceMappingURL=delegationWorkBranch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegationWorkBranch.d.ts","sourceRoot":"","sources":["../../src/agents/delegationWorkBranch.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,MAAM,EAEZ,MAAM,qBAAqB,CAAA;AAyB5B,gGAAgG;AAChG,MAAM,WAAW,wBAAwB;IACvC,2FAA2F;IAC3F,gBAAgB,EAAE,0BAA0B,GAAG,SAAS,CAAA;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,wFAAwF;AACxF,MAAM,WAAW,2BAA2B;IAC1C,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,OAAO,EAAE,OAAO,CAAA;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,wBAAwB,EAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,IAAI,CAAC,CA8Cf"}