@ai-agent-forge/plugin-multi-agent 0.88.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 (70) hide show
  1. package/README.md +3 -0
  2. package/dist/child-facade.d.ts +44 -0
  3. package/dist/child-facade.d.ts.map +1 -0
  4. package/dist/child-facade.js +56 -0
  5. package/dist/child-facade.js.map +1 -0
  6. package/dist/dependency-wiring.d.ts +48 -0
  7. package/dist/dependency-wiring.d.ts.map +1 -0
  8. package/dist/dependency-wiring.js +56 -0
  9. package/dist/dependency-wiring.js.map +1 -0
  10. package/dist/domain-recovery.d.ts +60 -0
  11. package/dist/domain-recovery.d.ts.map +1 -0
  12. package/dist/domain-recovery.js +94 -0
  13. package/dist/domain-recovery.js.map +1 -0
  14. package/dist/first-roles.d.ts +30 -0
  15. package/dist/first-roles.d.ts.map +1 -0
  16. package/dist/first-roles.js +83 -0
  17. package/dist/first-roles.js.map +1 -0
  18. package/dist/goal-plugin.d.ts +38 -0
  19. package/dist/goal-plugin.d.ts.map +1 -0
  20. package/dist/goal-plugin.js +132 -0
  21. package/dist/goal-plugin.js.map +1 -0
  22. package/dist/identity.d.ts +29 -0
  23. package/dist/identity.d.ts.map +1 -0
  24. package/dist/identity.js +22 -0
  25. package/dist/identity.js.map +1 -0
  26. package/dist/index.d.ts +23 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +41 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/plan-plugin.d.ts +56 -0
  31. package/dist/plan-plugin.d.ts.map +1 -0
  32. package/dist/plan-plugin.js +169 -0
  33. package/dist/plan-plugin.js.map +1 -0
  34. package/dist/policy-manifest.d.ts +53 -0
  35. package/dist/policy-manifest.d.ts.map +1 -0
  36. package/dist/policy-manifest.js +66 -0
  37. package/dist/policy-manifest.js.map +1 -0
  38. package/dist/role-assembly.d.ts +47 -0
  39. package/dist/role-assembly.d.ts.map +1 -0
  40. package/dist/role-assembly.js +31 -0
  41. package/dist/role-assembly.js.map +1 -0
  42. package/dist/role-catalog.d.ts +43 -0
  43. package/dist/role-catalog.d.ts.map +1 -0
  44. package/dist/role-catalog.js +132 -0
  45. package/dist/role-catalog.js.map +1 -0
  46. package/dist/role-orchestrator.d.ts +123 -0
  47. package/dist/role-orchestrator.d.ts.map +1 -0
  48. package/dist/role-orchestrator.js +228 -0
  49. package/dist/role-orchestrator.js.map +1 -0
  50. package/dist/role-runner.d.ts +99 -0
  51. package/dist/role-runner.d.ts.map +1 -0
  52. package/dist/role-runner.js +100 -0
  53. package/dist/role-runner.js.map +1 -0
  54. package/dist/root-facade.d.ts +36 -0
  55. package/dist/root-facade.d.ts.map +1 -0
  56. package/dist/root-facade.js +58 -0
  57. package/dist/root-facade.js.map +1 -0
  58. package/dist/skill-discovery-wiring.d.ts +58 -0
  59. package/dist/skill-discovery-wiring.d.ts.map +1 -0
  60. package/dist/skill-discovery-wiring.js +80 -0
  61. package/dist/skill-discovery-wiring.js.map +1 -0
  62. package/dist/state-store.d.ts +62 -0
  63. package/dist/state-store.d.ts.map +1 -0
  64. package/dist/state-store.js +158 -0
  65. package/dist/state-store.js.map +1 -0
  66. package/dist/todo-plugin.d.ts +47 -0
  67. package/dist/todo-plugin.d.ts.map +1 -0
  68. package/dist/todo-plugin.js +147 -0
  69. package/dist/todo-plugin.js.map +1 -0
  70. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @agent-forge/plugin-multi-agent
2
+
3
+ Agent Forge 的多 Agent 编排插件库(D-082 批 4):角色目录/执行器/装配/编排器、root/child 门面适配器、基于 transition store 的 Goal/Plan/Todo 领域插件、依赖与 Skill/MCP 发现接线、默认策略清单与领域恢复,逻辑自宿主 `packages/agent-forge/src/profile/` 逐字迁出,i18n 经 `@agent-forge/host/i18n` 供给键注入。
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Child Agent lifecycle adapter (2.2b).
3
+ *
4
+ * Spawns child agents bound to their parent via the composition's
5
+ * `parentInstanceId`, with cancel/wait and an independently replayable result
6
+ * reference (`childInstanceId` + `revision`). Only the public
7
+ * `AgentCompositionAPI` is consumed.
8
+ */
9
+ import type { AgentCompositionAPI } from "@agent-forge/plugin-sdk";
10
+ export interface ChildAgentResultRefV1 {
11
+ readonly childInstanceId: string;
12
+ readonly revision: number;
13
+ }
14
+ export interface ChildAgentTurnV1<TResult = unknown> {
15
+ readonly childInstanceId: string;
16
+ /** Stable, replayable identity of the turn result. */
17
+ readonly resultRef: () => ChildAgentResultRefV1 | undefined;
18
+ wait(): Promise<{
19
+ readonly status: "succeeded" | "failed" | "cancelled" | "disposed";
20
+ readonly output?: TResult;
21
+ readonly revision: number;
22
+ readonly error?: Error;
23
+ }>;
24
+ cancel(reason?: string): Promise<void>;
25
+ }
26
+ export interface ChildAgentFacadeV1<TInput = unknown, TResult = unknown> {
27
+ readonly childDefinitionId: string;
28
+ spawnChild(input: TInput, options?: {
29
+ readonly signal?: AbortSignal;
30
+ }): ChildAgentTurnV1<TResult>;
31
+ }
32
+ export declare function createChildAgentFacade<TInput, TResult = unknown>(options: {
33
+ readonly composition: AgentCompositionAPI;
34
+ readonly parentInstanceId: string;
35
+ readonly childDefinitionId?: string;
36
+ readonly childVersion?: number;
37
+ readonly owner?: string;
38
+ /** The child turn implementation (deterministic in tests). */
39
+ readonly run: (input: TInput, context: {
40
+ readonly parentInstanceId: string;
41
+ readonly signal?: AbortSignal;
42
+ }) => Promise<TResult> | TResult;
43
+ }): ChildAgentFacadeV1<TInput, TResult>;
44
+ //# sourceMappingURL=child-facade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-facade.d.ts","sourceRoot":"","sources":["../src/child-facade.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB,CAAC,OAAO,GAAG,OAAO;IAClD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,qBAAqB,GAAG,SAAS,CAAC;IAC5D,IAAI,IAAI,OAAO,CAAC;QACf,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;QACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;KACvB,CAAC,CAAC;IACH,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IACtE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAClG;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,EAAE,OAAO,EAAE;IAC1E,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;IAC1C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,KACzE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAChC,GAAG,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,CAwDtC","sourcesContent":["/**\n * Child Agent lifecycle adapter (2.2b).\n *\n * Spawns child agents bound to their parent via the composition's\n * `parentInstanceId`, with cancel/wait and an independently replayable result\n * reference (`childInstanceId` + `revision`). Only the public\n * `AgentCompositionAPI` is consumed.\n */\nimport type { AgentCompositionAPI } from \"@agent-forge/plugin-sdk\";\n\nexport interface ChildAgentResultRefV1 {\n\treadonly childInstanceId: string;\n\treadonly revision: number;\n}\n\nexport interface ChildAgentTurnV1<TResult = unknown> {\n\treadonly childInstanceId: string;\n\t/** Stable, replayable identity of the turn result. */\n\treadonly resultRef: () => ChildAgentResultRefV1 | undefined;\n\twait(): Promise<{\n\t\treadonly status: \"succeeded\" | \"failed\" | \"cancelled\" | \"disposed\";\n\t\treadonly output?: TResult;\n\t\treadonly revision: number;\n\t\treadonly error?: Error;\n\t}>;\n\tcancel(reason?: string): Promise<void>;\n}\n\nexport interface ChildAgentFacadeV1<TInput = unknown, TResult = unknown> {\n\treadonly childDefinitionId: string;\n\tspawnChild(input: TInput, options?: { readonly signal?: AbortSignal }): ChildAgentTurnV1<TResult>;\n}\n\nexport function createChildAgentFacade<TInput, TResult = unknown>(options: {\n\treadonly composition: AgentCompositionAPI;\n\treadonly parentInstanceId: string;\n\treadonly childDefinitionId?: string;\n\treadonly childVersion?: number;\n\treadonly owner?: string;\n\t/** The child turn implementation (deterministic in tests). */\n\treadonly run: (\n\t\tinput: TInput,\n\t\tcontext: { readonly parentInstanceId: string; readonly signal?: AbortSignal },\n\t) => Promise<TResult> | TResult;\n}): ChildAgentFacadeV1<TInput, TResult> {\n\tconst childDefinitionId = options.childDefinitionId ?? \"child-agent-controller\";\n\tconst owner = options.owner ?? \"parent\";\n\tconst definition = options.composition.define({\n\t\tid: childDefinitionId,\n\t\tversion: options.childVersion ?? 1,\n\t\trole: \"child-agent-controller\",\n\t\trun: (input: TInput, context: { readonly signal?: AbortSignal }) =>\n\t\t\toptions.run(input, { parentInstanceId: options.parentInstanceId, signal: context?.signal }),\n\t});\n\n\treturn {\n\t\tchildDefinitionId,\n\t\tspawnChild(input, spawnOptions): ChildAgentTurnV1<TResult> {\n\t\t\tconst instance = options.composition.create<TInput, TResult>(definition, {\n\t\t\t\tparentInstanceId: options.parentInstanceId,\n\t\t\t});\n\t\t\tconst childInstanceId = instance.id;\n\t\t\tlet settledRevision: number | undefined;\n\t\t\tconst leasePromise = instance.acquireControllerLease(owner);\n\t\t\tconst runPromise = leasePromise.then(async (lease) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst operation = instance.run(input, { lease, signal: spawnOptions?.signal });\n\t\t\t\t\tawait operation.result;\n\t\t\t\t} catch {\n\t\t\t\t\t// run() can reject when the instance was cancelled or disposed\n\t\t\t\t\t// mid-flight; instance.wait() below yields the terminal status.\n\t\t\t\t}\n\t\t\t\tconst instanceResult = await instance.wait();\n\t\t\t\tsettledRevision = instanceResult.revision;\n\t\t\t\tif (lease.isActive()) await lease.release();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: instanceResult.status,\n\t\t\t\t\t...(instanceResult.output !== undefined ? { output: instanceResult.output } : {}),\n\t\t\t\t\t...(instanceResult.error !== undefined\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\tinstanceResult.error instanceof Error\n\t\t\t\t\t\t\t\t\t\t? instanceResult.error\n\t\t\t\t\t\t\t\t\t\t: new Error(String(instanceResult.error)),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\trevision: instanceResult.revision,\n\t\t\t\t};\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tchildInstanceId,\n\t\t\t\tresultRef: () =>\n\t\t\t\t\tsettledRevision === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: Object.freeze({ childInstanceId, revision: settledRevision }),\n\t\t\t\twait: () => runPromise,\n\t\t\t\tcancel: (reason?: string) => instance.cancel(reason),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,56 @@
1
+ export function createChildAgentFacade(options) {
2
+ const childDefinitionId = options.childDefinitionId ?? "child-agent-controller";
3
+ const owner = options.owner ?? "parent";
4
+ const definition = options.composition.define({
5
+ id: childDefinitionId,
6
+ version: options.childVersion ?? 1,
7
+ role: "child-agent-controller",
8
+ run: (input, context) => options.run(input, { parentInstanceId: options.parentInstanceId, signal: context?.signal }),
9
+ });
10
+ return {
11
+ childDefinitionId,
12
+ spawnChild(input, spawnOptions) {
13
+ const instance = options.composition.create(definition, {
14
+ parentInstanceId: options.parentInstanceId,
15
+ });
16
+ const childInstanceId = instance.id;
17
+ let settledRevision;
18
+ const leasePromise = instance.acquireControllerLease(owner);
19
+ const runPromise = leasePromise.then(async (lease) => {
20
+ try {
21
+ const operation = instance.run(input, { lease, signal: spawnOptions?.signal });
22
+ await operation.result;
23
+ }
24
+ catch {
25
+ // run() can reject when the instance was cancelled or disposed
26
+ // mid-flight; instance.wait() below yields the terminal status.
27
+ }
28
+ const instanceResult = await instance.wait();
29
+ settledRevision = instanceResult.revision;
30
+ if (lease.isActive())
31
+ await lease.release();
32
+ return {
33
+ status: instanceResult.status,
34
+ ...(instanceResult.output !== undefined ? { output: instanceResult.output } : {}),
35
+ ...(instanceResult.error !== undefined
36
+ ? {
37
+ error: instanceResult.error instanceof Error
38
+ ? instanceResult.error
39
+ : new Error(String(instanceResult.error)),
40
+ }
41
+ : {}),
42
+ revision: instanceResult.revision,
43
+ };
44
+ });
45
+ return {
46
+ childInstanceId,
47
+ resultRef: () => settledRevision === undefined
48
+ ? undefined
49
+ : Object.freeze({ childInstanceId, revision: settledRevision }),
50
+ wait: () => runPromise,
51
+ cancel: (reason) => instance.cancel(reason),
52
+ };
53
+ },
54
+ };
55
+ }
56
+ //# sourceMappingURL=child-facade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-facade.js","sourceRoot":"","sources":["../src/child-facade.ts"],"names":[],"mappings":"AAiCA,MAAM,UAAU,sBAAsB,CAA4B,OAWjE,EAAuC;IACvC,MAAM,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,wBAAwB,CAAC;IAChF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC;IACxC,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC;QAC7C,EAAE,EAAE,iBAAiB;QACrB,OAAO,EAAE,OAAO,CAAC,YAAY,IAAI,CAAC;QAClC,IAAI,EAAE,wBAAwB;QAC9B,GAAG,EAAE,CAAC,KAAa,EAAE,OAA0C,EAAE,EAAE,CAClE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;KAC5F,CAAC,CAAC;IAEH,OAAO;QACN,iBAAiB;QACjB,UAAU,CAAC,KAAK,EAAE,YAAY,EAA6B;YAC1D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,CAAkB,UAAU,EAAE;gBACxE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC1C,CAAC,CAAC;YACH,MAAM,eAAe,GAAG,QAAQ,CAAC,EAAE,CAAC;YACpC,IAAI,eAAmC,CAAC;YACxC,MAAM,YAAY,GAAG,QAAQ,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;YAC5D,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;gBACrD,IAAI,CAAC;oBACJ,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;oBAC/E,MAAM,SAAS,CAAC,MAAM,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACR,+DAA+D;oBAC/D,gEAAgE;gBACjE,CAAC;gBACD,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC7C,eAAe,GAAG,cAAc,CAAC,QAAQ,CAAC;gBAC1C,IAAI,KAAK,CAAC,QAAQ,EAAE;oBAAE,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC5C,OAAO;oBACN,MAAM,EAAE,cAAc,CAAC,MAAM;oBAC7B,GAAG,CAAC,cAAc,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACjF,GAAG,CAAC,cAAc,CAAC,KAAK,KAAK,SAAS;wBACrC,CAAC,CAAC;4BACA,KAAK,EACJ,cAAc,CAAC,KAAK,YAAY,KAAK;gCACpC,CAAC,CAAC,cAAc,CAAC,KAAK;gCACtB,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;yBAC3C;wBACF,CAAC,CAAC,EAAE,CAAC;oBACN,QAAQ,EAAE,cAAc,CAAC,QAAQ;iBACjC,CAAC;YAAA,CACF,CAAC,CAAC;YACH,OAAO;gBACN,eAAe;gBACf,SAAS,EAAE,GAAG,EAAE,CACf,eAAe,KAAK,SAAS;oBAC5B,CAAC,CAAC,SAAS;oBACX,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC;gBACjE,IAAI,EAAE,GAAG,EAAE,CAAC,UAAU;gBACtB,MAAM,EAAE,CAAC,MAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;aACpD,CAAC;QAAA,CACF;KACD,CAAC;AAAA,CACF","sourcesContent":["/**\n * Child Agent lifecycle adapter (2.2b).\n *\n * Spawns child agents bound to their parent via the composition's\n * `parentInstanceId`, with cancel/wait and an independently replayable result\n * reference (`childInstanceId` + `revision`). Only the public\n * `AgentCompositionAPI` is consumed.\n */\nimport type { AgentCompositionAPI } from \"@agent-forge/plugin-sdk\";\n\nexport interface ChildAgentResultRefV1 {\n\treadonly childInstanceId: string;\n\treadonly revision: number;\n}\n\nexport interface ChildAgentTurnV1<TResult = unknown> {\n\treadonly childInstanceId: string;\n\t/** Stable, replayable identity of the turn result. */\n\treadonly resultRef: () => ChildAgentResultRefV1 | undefined;\n\twait(): Promise<{\n\t\treadonly status: \"succeeded\" | \"failed\" | \"cancelled\" | \"disposed\";\n\t\treadonly output?: TResult;\n\t\treadonly revision: number;\n\t\treadonly error?: Error;\n\t}>;\n\tcancel(reason?: string): Promise<void>;\n}\n\nexport interface ChildAgentFacadeV1<TInput = unknown, TResult = unknown> {\n\treadonly childDefinitionId: string;\n\tspawnChild(input: TInput, options?: { readonly signal?: AbortSignal }): ChildAgentTurnV1<TResult>;\n}\n\nexport function createChildAgentFacade<TInput, TResult = unknown>(options: {\n\treadonly composition: AgentCompositionAPI;\n\treadonly parentInstanceId: string;\n\treadonly childDefinitionId?: string;\n\treadonly childVersion?: number;\n\treadonly owner?: string;\n\t/** The child turn implementation (deterministic in tests). */\n\treadonly run: (\n\t\tinput: TInput,\n\t\tcontext: { readonly parentInstanceId: string; readonly signal?: AbortSignal },\n\t) => Promise<TResult> | TResult;\n}): ChildAgentFacadeV1<TInput, TResult> {\n\tconst childDefinitionId = options.childDefinitionId ?? \"child-agent-controller\";\n\tconst owner = options.owner ?? \"parent\";\n\tconst definition = options.composition.define({\n\t\tid: childDefinitionId,\n\t\tversion: options.childVersion ?? 1,\n\t\trole: \"child-agent-controller\",\n\t\trun: (input: TInput, context: { readonly signal?: AbortSignal }) =>\n\t\t\toptions.run(input, { parentInstanceId: options.parentInstanceId, signal: context?.signal }),\n\t});\n\n\treturn {\n\t\tchildDefinitionId,\n\t\tspawnChild(input, spawnOptions): ChildAgentTurnV1<TResult> {\n\t\t\tconst instance = options.composition.create<TInput, TResult>(definition, {\n\t\t\t\tparentInstanceId: options.parentInstanceId,\n\t\t\t});\n\t\t\tconst childInstanceId = instance.id;\n\t\t\tlet settledRevision: number | undefined;\n\t\t\tconst leasePromise = instance.acquireControllerLease(owner);\n\t\t\tconst runPromise = leasePromise.then(async (lease) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst operation = instance.run(input, { lease, signal: spawnOptions?.signal });\n\t\t\t\t\tawait operation.result;\n\t\t\t\t} catch {\n\t\t\t\t\t// run() can reject when the instance was cancelled or disposed\n\t\t\t\t\t// mid-flight; instance.wait() below yields the terminal status.\n\t\t\t\t}\n\t\t\t\tconst instanceResult = await instance.wait();\n\t\t\t\tsettledRevision = instanceResult.revision;\n\t\t\t\tif (lease.isActive()) await lease.release();\n\t\t\t\treturn {\n\t\t\t\t\tstatus: instanceResult.status,\n\t\t\t\t\t...(instanceResult.output !== undefined ? { output: instanceResult.output } : {}),\n\t\t\t\t\t...(instanceResult.error !== undefined\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\t\tinstanceResult.error instanceof Error\n\t\t\t\t\t\t\t\t\t\t? instanceResult.error\n\t\t\t\t\t\t\t\t\t\t: new Error(String(instanceResult.error)),\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t\trevision: instanceResult.revision,\n\t\t\t\t};\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tchildInstanceId,\n\t\t\t\tresultRef: () =>\n\t\t\t\t\tsettledRevision === undefined\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: Object.freeze({ childInstanceId, revision: settledRevision }),\n\t\t\t\twait: () => runPromise,\n\t\t\t\tcancel: (reason?: string) => instance.cancel(reason),\n\t\t\t};\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Profile dependency wiring (2.5a) — resolves every resolved plugin's
3
+ * capability references (Skill/tool/MCP-capability IDs) against the host's
4
+ * available capability registry BEFORE a session is created.
5
+ *
6
+ * Resolution is exact-version: a reference is satisfied only when the
7
+ * registry offers the same capability ID at exactly the requested version.
8
+ * Missing or version-mismatched capabilities produce a structured `missing`
9
+ * result so session creation can fail up front — never a half-wired session.
10
+ * Plugins listed in `disabledPluginIds` are skipped as a whole (their
11
+ * references are reported as `skipped_disabled`), which lets a profile drop
12
+ * a plugin without touching the kernel or the other plugins. The same input
13
+ * always resolves identically.
14
+ */
15
+ import type { ResolvedPluginManifestV1 } from "./identity.ts";
16
+ export interface AvailableCapabilityV1 {
17
+ readonly id: string;
18
+ readonly version: number;
19
+ }
20
+ export interface WiredCapabilityV1 {
21
+ readonly id: string;
22
+ readonly version: number;
23
+ }
24
+ export interface PluginWiringResultV1 {
25
+ readonly pluginId: string;
26
+ readonly pluginVersion: string;
27
+ readonly status: "wired" | "skipped_disabled";
28
+ readonly wired: readonly WiredCapabilityV1[];
29
+ }
30
+ export interface MissingDependencyV1 {
31
+ readonly pluginId: string;
32
+ readonly capabilityId: string;
33
+ readonly requestedVersion: number;
34
+ /** Undefined when the capability is entirely absent from the registry. */
35
+ readonly availableVersion?: number;
36
+ readonly reason: "missing" | "version_mismatch";
37
+ }
38
+ export interface ProfileDependencyWiringV1 {
39
+ readonly status: "wired" | "missing";
40
+ readonly plugins: readonly PluginWiringResultV1[];
41
+ readonly missing: readonly MissingDependencyV1[];
42
+ }
43
+ export declare function wireProfileDependencies(input: {
44
+ readonly resolvedPlugins: readonly ResolvedPluginManifestV1[];
45
+ readonly availableCapabilities: readonly AvailableCapabilityV1[];
46
+ readonly disabledPluginIds?: readonly string[];
47
+ }): ProfileDependencyWiringV1;
48
+ //# sourceMappingURL=dependency-wiring.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-wiring.d.ts","sourceRoot":"","sources":["../src/dependency-wiring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,kBAAkB,CAAC;IAC9C,QAAQ,CAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAAC;CAChD;AAED,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC9C,QAAQ,CAAC,eAAe,EAAE,SAAS,wBAAwB,EAAE,CAAC;IAC9D,QAAQ,CAAC,qBAAqB,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjE,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/C,GAAG,yBAAyB,CAuD5B","sourcesContent":["/**\n * Profile dependency wiring (2.5a) — resolves every resolved plugin's\n * capability references (Skill/tool/MCP-capability IDs) against the host's\n * available capability registry BEFORE a session is created.\n *\n * Resolution is exact-version: a reference is satisfied only when the\n * registry offers the same capability ID at exactly the requested version.\n * Missing or version-mismatched capabilities produce a structured `missing`\n * result so session creation can fail up front — never a half-wired session.\n * Plugins listed in `disabledPluginIds` are skipped as a whole (their\n * references are reported as `skipped_disabled`), which lets a profile drop\n * a plugin without touching the kernel or the other plugins. The same input\n * always resolves identically.\n */\nimport type { ResolvedPluginManifestV1 } from \"./identity.ts\";\n\nexport interface AvailableCapabilityV1 {\n\treadonly id: string;\n\treadonly version: number;\n}\n\nexport interface WiredCapabilityV1 {\n\treadonly id: string;\n\treadonly version: number;\n}\n\nexport interface PluginWiringResultV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly status: \"wired\" | \"skipped_disabled\";\n\treadonly wired: readonly WiredCapabilityV1[];\n}\n\nexport interface MissingDependencyV1 {\n\treadonly pluginId: string;\n\treadonly capabilityId: string;\n\treadonly requestedVersion: number;\n\t/** Undefined when the capability is entirely absent from the registry. */\n\treadonly availableVersion?: number;\n\treadonly reason: \"missing\" | \"version_mismatch\";\n}\n\nexport interface ProfileDependencyWiringV1 {\n\treadonly status: \"wired\" | \"missing\";\n\treadonly plugins: readonly PluginWiringResultV1[];\n\treadonly missing: readonly MissingDependencyV1[];\n}\n\nexport function wireProfileDependencies(input: {\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\treadonly availableCapabilities: readonly AvailableCapabilityV1[];\n\treadonly disabledPluginIds?: readonly string[];\n}): ProfileDependencyWiringV1 {\n\tconst registry = new Map<string, number>();\n\tfor (const capability of input.availableCapabilities) {\n\t\tregistry.set(capability.id, capability.version);\n\t}\n\tconst disabled = new Set(input.disabledPluginIds ?? []);\n\n\tconst plugins: PluginWiringResultV1[] = [];\n\tconst missing: MissingDependencyV1[] = [];\n\n\tfor (const plugin of input.resolvedPlugins) {\n\t\tif (disabled.has(plugin.pluginId)) {\n\t\t\tplugins.push({\n\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\tpluginVersion: plugin.pluginVersion,\n\t\t\t\tstatus: \"skipped_disabled\",\n\t\t\t\twired: [],\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconst wired: WiredCapabilityV1[] = [];\n\t\tfor (const reference of plugin.capabilityRefs) {\n\t\t\tconst availableVersion = registry.get(reference.id);\n\t\t\tif (availableVersion === undefined) {\n\t\t\t\tmissing.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tcapabilityId: reference.id,\n\t\t\t\t\trequestedVersion: reference.version,\n\t\t\t\t\treason: \"missing\",\n\t\t\t\t});\n\t\t\t} else if (availableVersion !== reference.version) {\n\t\t\t\tmissing.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tcapabilityId: reference.id,\n\t\t\t\t\trequestedVersion: reference.version,\n\t\t\t\t\tavailableVersion,\n\t\t\t\t\treason: \"version_mismatch\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twired.push({ id: reference.id, version: reference.version });\n\t\t\t}\n\t\t}\n\t\tplugins.push({\n\t\t\tpluginId: plugin.pluginId,\n\t\t\tpluginVersion: plugin.pluginVersion,\n\t\t\tstatus: \"wired\",\n\t\t\twired,\n\t\t});\n\t}\n\n\treturn {\n\t\tstatus: missing.length > 0 ? \"missing\" : \"wired\",\n\t\tplugins,\n\t\tmissing,\n\t};\n}\n"]}
@@ -0,0 +1,56 @@
1
+ export function wireProfileDependencies(input) {
2
+ const registry = new Map();
3
+ for (const capability of input.availableCapabilities) {
4
+ registry.set(capability.id, capability.version);
5
+ }
6
+ const disabled = new Set(input.disabledPluginIds ?? []);
7
+ const plugins = [];
8
+ const missing = [];
9
+ for (const plugin of input.resolvedPlugins) {
10
+ if (disabled.has(plugin.pluginId)) {
11
+ plugins.push({
12
+ pluginId: plugin.pluginId,
13
+ pluginVersion: plugin.pluginVersion,
14
+ status: "skipped_disabled",
15
+ wired: [],
16
+ });
17
+ continue;
18
+ }
19
+ const wired = [];
20
+ for (const reference of plugin.capabilityRefs) {
21
+ const availableVersion = registry.get(reference.id);
22
+ if (availableVersion === undefined) {
23
+ missing.push({
24
+ pluginId: plugin.pluginId,
25
+ capabilityId: reference.id,
26
+ requestedVersion: reference.version,
27
+ reason: "missing",
28
+ });
29
+ }
30
+ else if (availableVersion !== reference.version) {
31
+ missing.push({
32
+ pluginId: plugin.pluginId,
33
+ capabilityId: reference.id,
34
+ requestedVersion: reference.version,
35
+ availableVersion,
36
+ reason: "version_mismatch",
37
+ });
38
+ }
39
+ else {
40
+ wired.push({ id: reference.id, version: reference.version });
41
+ }
42
+ }
43
+ plugins.push({
44
+ pluginId: plugin.pluginId,
45
+ pluginVersion: plugin.pluginVersion,
46
+ status: "wired",
47
+ wired,
48
+ });
49
+ }
50
+ return {
51
+ status: missing.length > 0 ? "missing" : "wired",
52
+ plugins,
53
+ missing,
54
+ };
55
+ }
56
+ //# sourceMappingURL=dependency-wiring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dependency-wiring.js","sourceRoot":"","sources":["../src/dependency-wiring.ts"],"names":[],"mappings":"AAgDA,MAAM,UAAU,uBAAuB,CAAC,KAIvC,EAA6B;IAC7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,qBAAqB,EAAE,CAAC;QACtD,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,MAAM,OAAO,GAA0B,EAAE,CAAC;IAE1C,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC5C,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,MAAM,EAAE,kBAAkB;gBAC1B,KAAK,EAAE,EAAE;aACT,CAAC,CAAC;YACH,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAwB,EAAE,CAAC;QACtC,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC/C,MAAM,gBAAgB,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACpD,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACpC,OAAO,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,YAAY,EAAE,SAAS,CAAC,EAAE;oBAC1B,gBAAgB,EAAE,SAAS,CAAC,OAAO;oBACnC,MAAM,EAAE,SAAS;iBACjB,CAAC,CAAC;YACJ,CAAC;iBAAM,IAAI,gBAAgB,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;gBACnD,OAAO,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;oBACzB,YAAY,EAAE,SAAS,CAAC,EAAE;oBAC1B,gBAAgB,EAAE,SAAS,CAAC,OAAO;oBACnC,gBAAgB;oBAChB,MAAM,EAAE,kBAAkB;iBAC1B,CAAC,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,SAAS,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9D,CAAC;QACF,CAAC;QACD,OAAO,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,aAAa,EAAE,MAAM,CAAC,aAAa;YACnC,MAAM,EAAE,OAAO;YACf,KAAK;SACL,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACN,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;QAChD,OAAO;QACP,OAAO;KACP,CAAC;AAAA,CACF","sourcesContent":["/**\n * Profile dependency wiring (2.5a) — resolves every resolved plugin's\n * capability references (Skill/tool/MCP-capability IDs) against the host's\n * available capability registry BEFORE a session is created.\n *\n * Resolution is exact-version: a reference is satisfied only when the\n * registry offers the same capability ID at exactly the requested version.\n * Missing or version-mismatched capabilities produce a structured `missing`\n * result so session creation can fail up front — never a half-wired session.\n * Plugins listed in `disabledPluginIds` are skipped as a whole (their\n * references are reported as `skipped_disabled`), which lets a profile drop\n * a plugin without touching the kernel or the other plugins. The same input\n * always resolves identically.\n */\nimport type { ResolvedPluginManifestV1 } from \"./identity.ts\";\n\nexport interface AvailableCapabilityV1 {\n\treadonly id: string;\n\treadonly version: number;\n}\n\nexport interface WiredCapabilityV1 {\n\treadonly id: string;\n\treadonly version: number;\n}\n\nexport interface PluginWiringResultV1 {\n\treadonly pluginId: string;\n\treadonly pluginVersion: string;\n\treadonly status: \"wired\" | \"skipped_disabled\";\n\treadonly wired: readonly WiredCapabilityV1[];\n}\n\nexport interface MissingDependencyV1 {\n\treadonly pluginId: string;\n\treadonly capabilityId: string;\n\treadonly requestedVersion: number;\n\t/** Undefined when the capability is entirely absent from the registry. */\n\treadonly availableVersion?: number;\n\treadonly reason: \"missing\" | \"version_mismatch\";\n}\n\nexport interface ProfileDependencyWiringV1 {\n\treadonly status: \"wired\" | \"missing\";\n\treadonly plugins: readonly PluginWiringResultV1[];\n\treadonly missing: readonly MissingDependencyV1[];\n}\n\nexport function wireProfileDependencies(input: {\n\treadonly resolvedPlugins: readonly ResolvedPluginManifestV1[];\n\treadonly availableCapabilities: readonly AvailableCapabilityV1[];\n\treadonly disabledPluginIds?: readonly string[];\n}): ProfileDependencyWiringV1 {\n\tconst registry = new Map<string, number>();\n\tfor (const capability of input.availableCapabilities) {\n\t\tregistry.set(capability.id, capability.version);\n\t}\n\tconst disabled = new Set(input.disabledPluginIds ?? []);\n\n\tconst plugins: PluginWiringResultV1[] = [];\n\tconst missing: MissingDependencyV1[] = [];\n\n\tfor (const plugin of input.resolvedPlugins) {\n\t\tif (disabled.has(plugin.pluginId)) {\n\t\t\tplugins.push({\n\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\tpluginVersion: plugin.pluginVersion,\n\t\t\t\tstatus: \"skipped_disabled\",\n\t\t\t\twired: [],\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconst wired: WiredCapabilityV1[] = [];\n\t\tfor (const reference of plugin.capabilityRefs) {\n\t\t\tconst availableVersion = registry.get(reference.id);\n\t\t\tif (availableVersion === undefined) {\n\t\t\t\tmissing.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tcapabilityId: reference.id,\n\t\t\t\t\trequestedVersion: reference.version,\n\t\t\t\t\treason: \"missing\",\n\t\t\t\t});\n\t\t\t} else if (availableVersion !== reference.version) {\n\t\t\t\tmissing.push({\n\t\t\t\t\tpluginId: plugin.pluginId,\n\t\t\t\t\tcapabilityId: reference.id,\n\t\t\t\t\trequestedVersion: reference.version,\n\t\t\t\t\tavailableVersion,\n\t\t\t\t\treason: \"version_mismatch\",\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twired.push({ id: reference.id, version: reference.version });\n\t\t\t}\n\t\t}\n\t\tplugins.push({\n\t\t\tpluginId: plugin.pluginId,\n\t\t\tpluginVersion: plugin.pluginVersion,\n\t\t\tstatus: \"wired\",\n\t\t\twired,\n\t\t});\n\t}\n\n\treturn {\n\t\tstatus: missing.length > 0 ? \"missing\" : \"wired\",\n\t\tplugins,\n\t\tmissing,\n\t};\n}\n"]}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Domain recovery for branches and restarts (5.2a/5.2b).
3
+ *
4
+ * Branch recovery (5.2a): plans are bound to a branch via the 2.3c plugin;
5
+ * recovering a session on branch B must touch ONLY plans whose branch is B
6
+ * — other-branch plans stay ledgered but are reported as skipped and never
7
+ * written to. Restart recovery (5.2b): replaying a domain's history into a
8
+ * fresh store restores revision, evidence, and terminal state exactly; the
9
+ * recovery report records the before/after facts so a restart is auditable.
10
+ * Multi-Agent scheduling is out of scope.
11
+ */
12
+ import type { GoalPluginV1, GoalV1 } from "./goal-plugin.ts";
13
+ import type { PlanPluginV1, PlanV1 } from "./plan-plugin.ts";
14
+ import type { TodoPluginV1, TodoV1 } from "./todo-plugin.ts";
15
+ export interface PlanBranchRecoveryEntryV1 {
16
+ readonly planId: string;
17
+ readonly outcome: "recovered" | "terminal_kept" | "skipped_other_branch";
18
+ readonly branch: string;
19
+ readonly statusBefore: string;
20
+ }
21
+ export interface PlanBranchRecoveryReportV1 {
22
+ readonly currentBranch: string;
23
+ readonly entries: readonly PlanBranchRecoveryEntryV1[];
24
+ }
25
+ export interface DomainRestartRecordV1 {
26
+ readonly domain: "goal" | "plan" | "todo";
27
+ readonly entityId: string;
28
+ readonly statusAfter: string;
29
+ readonly revisionAfter: number;
30
+ readonly evidenceCount: number;
31
+ readonly terminal: boolean;
32
+ }
33
+ export interface DomainRestartReportV1 {
34
+ readonly records: readonly DomainRestartRecordV1[];
35
+ /** True when every restored entity matches the state it had before the restart. */
36
+ readonly identical: boolean;
37
+ }
38
+ /**
39
+ * 5.2a — recovers plans for the current branch. Plans on other branches are
40
+ * reported and left untouched (no scans, no writes). Terminal plans on the
41
+ * current branch are kept as-is; draft/active plans are recovered through
42
+ * the plugin's own ledger-derived state (no shadow writes).
43
+ */
44
+ export declare function recoverPlansForBranch(plans: readonly PlanV1[], currentBranch: string): PlanBranchRecoveryReportV1;
45
+ /**
46
+ * 5.2b — restores goal/plan/todo state after a restart by replaying each
47
+ * entity's history into a fresh store (the caller performs the replay via
48
+ * the plugin attached to the rebuilt store) and comparing against the
49
+ * pre-restart snapshots.
50
+ */
51
+ export declare function buildRestartReport(snapshots: {
52
+ readonly goal?: readonly GoalV1[];
53
+ readonly plan?: readonly PlanV1[];
54
+ readonly todo?: readonly TodoV1[];
55
+ }, restored: {
56
+ readonly goals: GoalPluginV1;
57
+ readonly plans: PlanPluginV1;
58
+ readonly todos: TodoPluginV1;
59
+ }, owner: string): DomainRestartReportV1;
60
+ //# sourceMappingURL=domain-recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-recovery.d.ts","sourceRoot":"","sources":["../src/domain-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,eAAe,GAAG,sBAAsB,CAAC;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,mFAAmF;IACnF,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC5B;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,0BAA0B,CAmBjH;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CACjC,SAAS,EAAE;IACV,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC,EACD,QAAQ,EAAE;IACT,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC7B,EACD,KAAK,EAAE,MAAM,GACX,qBAAqB,CA4FvB","sourcesContent":["/**\n * Domain recovery for branches and restarts (5.2a/5.2b).\n *\n * Branch recovery (5.2a): plans are bound to a branch via the 2.3c plugin;\n * recovering a session on branch B must touch ONLY plans whose branch is B\n * — other-branch plans stay ledgered but are reported as skipped and never\n * written to. Restart recovery (5.2b): replaying a domain's history into a\n * fresh store restores revision, evidence, and terminal state exactly; the\n * recovery report records the before/after facts so a restart is auditable.\n * Multi-Agent scheduling is out of scope.\n */\nimport type { GoalPluginV1, GoalV1 } from \"./goal-plugin.ts\";\nimport type { PlanPluginV1, PlanV1 } from \"./plan-plugin.ts\";\nimport type { TodoPluginV1, TodoV1 } from \"./todo-plugin.ts\";\n\nexport interface PlanBranchRecoveryEntryV1 {\n\treadonly planId: string;\n\treadonly outcome: \"recovered\" | \"terminal_kept\" | \"skipped_other_branch\";\n\treadonly branch: string;\n\treadonly statusBefore: string;\n}\n\nexport interface PlanBranchRecoveryReportV1 {\n\treadonly currentBranch: string;\n\treadonly entries: readonly PlanBranchRecoveryEntryV1[];\n}\n\nexport interface DomainRestartRecordV1 {\n\treadonly domain: \"goal\" | \"plan\" | \"todo\";\n\treadonly entityId: string;\n\treadonly statusAfter: string;\n\treadonly revisionAfter: number;\n\treadonly evidenceCount: number;\n\treadonly terminal: boolean;\n}\n\nexport interface DomainRestartReportV1 {\n\treadonly records: readonly DomainRestartRecordV1[];\n\t/** True when every restored entity matches the state it had before the restart. */\n\treadonly identical: boolean;\n}\n\nconst GOAL_TERMINAL = new Set([\"achieved\", \"archived\"]);\nconst PLAN_TERMINAL = new Set([\"completed\", \"discarded\"]);\nconst TODO_TERMINAL = new Set([\"completed\", \"cancelled\"]);\n\n/**\n * 5.2a — recovers plans for the current branch. Plans on other branches are\n * reported and left untouched (no scans, no writes). Terminal plans on the\n * current branch are kept as-is; draft/active plans are recovered through\n * the plugin's own ledger-derived state (no shadow writes).\n */\nexport function recoverPlansForBranch(plans: readonly PlanV1[], currentBranch: string): PlanBranchRecoveryReportV1 {\n\tconst entries = plans.map((plan): PlanBranchRecoveryEntryV1 => {\n\t\tconst terminal = PLAN_TERMINAL.has(plan.status);\n\t\tif (plan.branch !== currentBranch) {\n\t\t\treturn {\n\t\t\t\tplanId: plan.planId,\n\t\t\t\toutcome: \"skipped_other_branch\",\n\t\t\t\tbranch: plan.branch,\n\t\t\t\tstatusBefore: plan.status,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tplanId: plan.planId,\n\t\t\toutcome: terminal ? \"terminal_kept\" : \"recovered\",\n\t\t\tbranch: plan.branch,\n\t\t\tstatusBefore: plan.status,\n\t\t};\n\t});\n\treturn { currentBranch, entries: Object.freeze(entries) };\n}\n\n/**\n * 5.2b — restores goal/plan/todo state after a restart by replaying each\n * entity's history into a fresh store (the caller performs the replay via\n * the plugin attached to the rebuilt store) and comparing against the\n * pre-restart snapshots.\n */\nexport function buildRestartReport(\n\tsnapshots: {\n\t\treadonly goal?: readonly GoalV1[];\n\t\treadonly plan?: readonly PlanV1[];\n\t\treadonly todo?: readonly TodoV1[];\n\t},\n\trestored: {\n\t\treadonly goals: GoalPluginV1;\n\t\treadonly plans: PlanPluginV1;\n\t\treadonly todos: TodoPluginV1;\n\t},\n\towner: string,\n): DomainRestartReportV1 {\n\tconst records: DomainRestartRecordV1[] = [];\n\tlet identical = true;\n\tconst compare = (\n\t\tdomain: \"goal\" | \"plan\" | \"todo\",\n\t\tentityId: string,\n\t\tstatusAfter: string,\n\t\trevisionAfter: number,\n\t\tevidenceCount: number,\n\t\tterminal: boolean,\n\t\tstatusBefore: string,\n\t\trevisionBefore: number,\n\t): void => {\n\t\tif (statusAfter !== statusBefore || revisionAfter !== revisionBefore) identical = false;\n\t\trecords.push({ domain, entityId, statusAfter, revisionAfter, evidenceCount, terminal });\n\t};\n\tfor (const goal of snapshots.goal ?? []) {\n\t\tconst after = restored.goals.get(goal.goalId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"goal\",\n\t\t\t\tentityId: goal.goalId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"goal\",\n\t\t\tgoal.goalId,\n\t\t\tafter.status,\n\t\t\tafter.revision,\n\t\t\tafter.evidence.length,\n\t\t\tGOAL_TERMINAL.has(after.status),\n\t\t\tgoal.status,\n\t\t\tgoal.revision,\n\t\t);\n\t}\n\tfor (const plan of snapshots.plan ?? []) {\n\t\tconst after = restored.plans.get(plan.planId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"plan\",\n\t\t\t\tentityId: plan.planId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"plan\",\n\t\t\tplan.planId,\n\t\t\tafter.status,\n\t\t\tafter.revisionNo,\n\t\t\tafter.evidence.length,\n\t\t\tPLAN_TERMINAL.has(after.status),\n\t\t\tplan.status,\n\t\t\tplan.revisionNo,\n\t\t);\n\t}\n\tfor (const todo of snapshots.todo ?? []) {\n\t\tconst after = restored.todos.get(todo.todoId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"todo\",\n\t\t\t\tentityId: todo.todoId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"todo\",\n\t\t\ttodo.todoId,\n\t\t\tafter.status,\n\t\t\tafter.revision,\n\t\t\tafter.evidence.length,\n\t\t\tTODO_TERMINAL.has(after.status),\n\t\t\ttodo.status,\n\t\t\ttodo.revision,\n\t\t);\n\t}\n\treturn { records: Object.freeze(records), identical };\n}\n"]}
@@ -0,0 +1,94 @@
1
+ const GOAL_TERMINAL = new Set(["achieved", "archived"]);
2
+ const PLAN_TERMINAL = new Set(["completed", "discarded"]);
3
+ const TODO_TERMINAL = new Set(["completed", "cancelled"]);
4
+ /**
5
+ * 5.2a — recovers plans for the current branch. Plans on other branches are
6
+ * reported and left untouched (no scans, no writes). Terminal plans on the
7
+ * current branch are kept as-is; draft/active plans are recovered through
8
+ * the plugin's own ledger-derived state (no shadow writes).
9
+ */
10
+ export function recoverPlansForBranch(plans, currentBranch) {
11
+ const entries = plans.map((plan) => {
12
+ const terminal = PLAN_TERMINAL.has(plan.status);
13
+ if (plan.branch !== currentBranch) {
14
+ return {
15
+ planId: plan.planId,
16
+ outcome: "skipped_other_branch",
17
+ branch: plan.branch,
18
+ statusBefore: plan.status,
19
+ };
20
+ }
21
+ return {
22
+ planId: plan.planId,
23
+ outcome: terminal ? "terminal_kept" : "recovered",
24
+ branch: plan.branch,
25
+ statusBefore: plan.status,
26
+ };
27
+ });
28
+ return { currentBranch, entries: Object.freeze(entries) };
29
+ }
30
+ /**
31
+ * 5.2b — restores goal/plan/todo state after a restart by replaying each
32
+ * entity's history into a fresh store (the caller performs the replay via
33
+ * the plugin attached to the rebuilt store) and comparing against the
34
+ * pre-restart snapshots.
35
+ */
36
+ export function buildRestartReport(snapshots, restored, owner) {
37
+ const records = [];
38
+ let identical = true;
39
+ const compare = (domain, entityId, statusAfter, revisionAfter, evidenceCount, terminal, statusBefore, revisionBefore) => {
40
+ if (statusAfter !== statusBefore || revisionAfter !== revisionBefore)
41
+ identical = false;
42
+ records.push({ domain, entityId, statusAfter, revisionAfter, evidenceCount, terminal });
43
+ };
44
+ for (const goal of snapshots.goal ?? []) {
45
+ const after = restored.goals.get(goal.goalId, owner);
46
+ if (!after) {
47
+ identical = false;
48
+ records.push({
49
+ domain: "goal",
50
+ entityId: goal.goalId,
51
+ statusAfter: "missing",
52
+ revisionAfter: 0,
53
+ evidenceCount: 0,
54
+ terminal: false,
55
+ });
56
+ continue;
57
+ }
58
+ compare("goal", goal.goalId, after.status, after.revision, after.evidence.length, GOAL_TERMINAL.has(after.status), goal.status, goal.revision);
59
+ }
60
+ for (const plan of snapshots.plan ?? []) {
61
+ const after = restored.plans.get(plan.planId, owner);
62
+ if (!after) {
63
+ identical = false;
64
+ records.push({
65
+ domain: "plan",
66
+ entityId: plan.planId,
67
+ statusAfter: "missing",
68
+ revisionAfter: 0,
69
+ evidenceCount: 0,
70
+ terminal: false,
71
+ });
72
+ continue;
73
+ }
74
+ compare("plan", plan.planId, after.status, after.revisionNo, after.evidence.length, PLAN_TERMINAL.has(after.status), plan.status, plan.revisionNo);
75
+ }
76
+ for (const todo of snapshots.todo ?? []) {
77
+ const after = restored.todos.get(todo.todoId, owner);
78
+ if (!after) {
79
+ identical = false;
80
+ records.push({
81
+ domain: "todo",
82
+ entityId: todo.todoId,
83
+ statusAfter: "missing",
84
+ revisionAfter: 0,
85
+ evidenceCount: 0,
86
+ terminal: false,
87
+ });
88
+ continue;
89
+ }
90
+ compare("todo", todo.todoId, after.status, after.revision, after.evidence.length, TODO_TERMINAL.has(after.status), todo.status, todo.revision);
91
+ }
92
+ return { records: Object.freeze(records), identical };
93
+ }
94
+ //# sourceMappingURL=domain-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domain-recovery.js","sourceRoot":"","sources":["../src/domain-recovery.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Domain recovery for branches and restarts (5.2a/5.2b).\n *\n * Branch recovery (5.2a): plans are bound to a branch via the 2.3c plugin;\n * recovering a session on branch B must touch ONLY plans whose branch is B\n * — other-branch plans stay ledgered but are reported as skipped and never\n * written to. Restart recovery (5.2b): replaying a domain's history into a\n * fresh store restores revision, evidence, and terminal state exactly; the\n * recovery report records the before/after facts so a restart is auditable.\n * Multi-Agent scheduling is out of scope.\n */\nimport type { GoalPluginV1, GoalV1 } from \"./goal-plugin.ts\";\nimport type { PlanPluginV1, PlanV1 } from \"./plan-plugin.ts\";\nimport type { TodoPluginV1, TodoV1 } from \"./todo-plugin.ts\";\n\nexport interface PlanBranchRecoveryEntryV1 {\n\treadonly planId: string;\n\treadonly outcome: \"recovered\" | \"terminal_kept\" | \"skipped_other_branch\";\n\treadonly branch: string;\n\treadonly statusBefore: string;\n}\n\nexport interface PlanBranchRecoveryReportV1 {\n\treadonly currentBranch: string;\n\treadonly entries: readonly PlanBranchRecoveryEntryV1[];\n}\n\nexport interface DomainRestartRecordV1 {\n\treadonly domain: \"goal\" | \"plan\" | \"todo\";\n\treadonly entityId: string;\n\treadonly statusAfter: string;\n\treadonly revisionAfter: number;\n\treadonly evidenceCount: number;\n\treadonly terminal: boolean;\n}\n\nexport interface DomainRestartReportV1 {\n\treadonly records: readonly DomainRestartRecordV1[];\n\t/** True when every restored entity matches the state it had before the restart. */\n\treadonly identical: boolean;\n}\n\nconst GOAL_TERMINAL = new Set([\"achieved\", \"archived\"]);\nconst PLAN_TERMINAL = new Set([\"completed\", \"discarded\"]);\nconst TODO_TERMINAL = new Set([\"completed\", \"cancelled\"]);\n\n/**\n * 5.2a — recovers plans for the current branch. Plans on other branches are\n * reported and left untouched (no scans, no writes). Terminal plans on the\n * current branch are kept as-is; draft/active plans are recovered through\n * the plugin's own ledger-derived state (no shadow writes).\n */\nexport function recoverPlansForBranch(plans: readonly PlanV1[], currentBranch: string): PlanBranchRecoveryReportV1 {\n\tconst entries = plans.map((plan): PlanBranchRecoveryEntryV1 => {\n\t\tconst terminal = PLAN_TERMINAL.has(plan.status);\n\t\tif (plan.branch !== currentBranch) {\n\t\t\treturn {\n\t\t\t\tplanId: plan.planId,\n\t\t\t\toutcome: \"skipped_other_branch\",\n\t\t\t\tbranch: plan.branch,\n\t\t\t\tstatusBefore: plan.status,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tplanId: plan.planId,\n\t\t\toutcome: terminal ? \"terminal_kept\" : \"recovered\",\n\t\t\tbranch: plan.branch,\n\t\t\tstatusBefore: plan.status,\n\t\t};\n\t});\n\treturn { currentBranch, entries: Object.freeze(entries) };\n}\n\n/**\n * 5.2b — restores goal/plan/todo state after a restart by replaying each\n * entity's history into a fresh store (the caller performs the replay via\n * the plugin attached to the rebuilt store) and comparing against the\n * pre-restart snapshots.\n */\nexport function buildRestartReport(\n\tsnapshots: {\n\t\treadonly goal?: readonly GoalV1[];\n\t\treadonly plan?: readonly PlanV1[];\n\t\treadonly todo?: readonly TodoV1[];\n\t},\n\trestored: {\n\t\treadonly goals: GoalPluginV1;\n\t\treadonly plans: PlanPluginV1;\n\t\treadonly todos: TodoPluginV1;\n\t},\n\towner: string,\n): DomainRestartReportV1 {\n\tconst records: DomainRestartRecordV1[] = [];\n\tlet identical = true;\n\tconst compare = (\n\t\tdomain: \"goal\" | \"plan\" | \"todo\",\n\t\tentityId: string,\n\t\tstatusAfter: string,\n\t\trevisionAfter: number,\n\t\tevidenceCount: number,\n\t\tterminal: boolean,\n\t\tstatusBefore: string,\n\t\trevisionBefore: number,\n\t): void => {\n\t\tif (statusAfter !== statusBefore || revisionAfter !== revisionBefore) identical = false;\n\t\trecords.push({ domain, entityId, statusAfter, revisionAfter, evidenceCount, terminal });\n\t};\n\tfor (const goal of snapshots.goal ?? []) {\n\t\tconst after = restored.goals.get(goal.goalId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"goal\",\n\t\t\t\tentityId: goal.goalId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"goal\",\n\t\t\tgoal.goalId,\n\t\t\tafter.status,\n\t\t\tafter.revision,\n\t\t\tafter.evidence.length,\n\t\t\tGOAL_TERMINAL.has(after.status),\n\t\t\tgoal.status,\n\t\t\tgoal.revision,\n\t\t);\n\t}\n\tfor (const plan of snapshots.plan ?? []) {\n\t\tconst after = restored.plans.get(plan.planId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"plan\",\n\t\t\t\tentityId: plan.planId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"plan\",\n\t\t\tplan.planId,\n\t\t\tafter.status,\n\t\t\tafter.revisionNo,\n\t\t\tafter.evidence.length,\n\t\t\tPLAN_TERMINAL.has(after.status),\n\t\t\tplan.status,\n\t\t\tplan.revisionNo,\n\t\t);\n\t}\n\tfor (const todo of snapshots.todo ?? []) {\n\t\tconst after = restored.todos.get(todo.todoId, owner);\n\t\tif (!after) {\n\t\t\tidentical = false;\n\t\t\trecords.push({\n\t\t\t\tdomain: \"todo\",\n\t\t\t\tentityId: todo.todoId,\n\t\t\t\tstatusAfter: \"missing\",\n\t\t\t\trevisionAfter: 0,\n\t\t\t\tevidenceCount: 0,\n\t\t\t\tterminal: false,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tcompare(\n\t\t\t\"todo\",\n\t\t\ttodo.todoId,\n\t\t\tafter.status,\n\t\t\tafter.revision,\n\t\t\tafter.evidence.length,\n\t\t\tTODO_TERMINAL.has(after.status),\n\t\t\ttodo.status,\n\t\t\ttodo.revision,\n\t\t);\n\t}\n\treturn { records: Object.freeze(records), identical };\n}\n"]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * First-batch role vertical slices (2.2c): 架构/实现/测试.
3
+ *
4
+ * Each role is a `RoleMetadataV1` registered in the role catalog plus a
5
+ * deterministic runner that only consumes the public facade — no private
6
+ * session access. These are the first vertical slices proving that a role can
7
+ * be declared, discovered, and executed end to end.
8
+ */
9
+ import type { RoleCatalogV1, RoleMetadataV1 } from "./role-catalog.ts";
10
+ export interface FirstBatchRoleV1 {
11
+ readonly metadata: RoleMetadataV1;
12
+ readonly run: (input: {
13
+ readonly spec: string;
14
+ }) => Promise<{
15
+ readonly result: string;
16
+ }>;
17
+ }
18
+ export declare const FIRST_BATCH_ROLES: readonly {
19
+ readonly metadata: RoleMetadataV1;
20
+ readonly run: FirstBatchRoleV1["run"];
21
+ }[];
22
+ /** Registers and returns the first-batch role vertical slices. */
23
+ export declare function createFirstBatchRoleSlices(catalog: RoleCatalogV1): {
24
+ readonly roles: readonly FirstBatchRoleV1[];
25
+ run(roleId: string, spec: string): Promise<{
26
+ readonly roleId: string;
27
+ readonly result: string;
28
+ }>;
29
+ };
30
+ //# sourceMappingURL=first-roles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"first-roles.d.ts","sourceRoot":"","sources":["../src/first-roles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGvE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzF;AAsCD,eAAO,MAAM,iBAAiB,EAAE,SAAS;IACxC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;CACtC,EAmBA,CAAC;AAEF,kEAAkE;AAClE,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,aAAa,GAAG;IACnE,QAAQ,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC5C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjG,CAeA","sourcesContent":["/**\n * First-batch role vertical slices (2.2c): 架构/实现/测试.\n *\n * Each role is a `RoleMetadataV1` registered in the role catalog plus a\n * deterministic runner that only consumes the public facade — no private\n * session access. These are the first vertical slices proving that a role can\n * be declared, discovered, and executed end to end.\n */\n\nimport { t } from \"@agent-forge/host/i18n\";\nimport type { RoleCatalogV1, RoleMetadataV1 } from \"./role-catalog.ts\";\nimport { validateRoleMetadataV1 } from \"./role-catalog.ts\";\n\nexport interface FirstBatchRoleV1 {\n\treadonly metadata: RoleMetadataV1;\n\treadonly run: (input: { readonly spec: string }) => Promise<{ readonly result: string }>;\n}\n\nconst ARCHITECTURE_ROLE: RoleMetadataV1 = {\n\troleId: \"architecture-analyst\",\n\ttitle: \"Architecture Analyst\",\n\tinput: { schemaId: \"role/architecture-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/architecture-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [{ capabilityId: \"tool.read\", capabilityVersion: 1, required: true }],\n\twritableScopes: [\"session\"],\n\tconcurrency: \"parallel\",\n\tcompletionCriteria: \"module structure findings recorded\",\n};\n\nconst IMPLEMENTER_ROLE: RoleMetadataV1 = {\n\troleId: \"implementer\",\n\ttitle: \"Implementer\",\n\tinput: { schemaId: \"role/implement-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/implement-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [\n\t\t{ capabilityId: \"tool.edit\", capabilityVersion: 1, required: true },\n\t\t{ capabilityId: \"tool.write\", capabilityVersion: 1, required: false },\n\t],\n\twritableScopes: [\"session\", \"cycle\"],\n\tconcurrency: \"single\",\n\tcompletionCriteria: \"patch produced for the requested spec\",\n};\n\nconst TEST_DESIGNER_ROLE: RoleMetadataV1 = {\n\troleId: \"test-designer\",\n\ttitle: \"Test Designer\",\n\tinput: { schemaId: \"role/test-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/test-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [{ capabilityId: \"tool.read\", capabilityVersion: 1, required: true }],\n\twritableScopes: [\"session\"],\n\tconcurrency: \"parallel\",\n\tcompletionCriteria: \"test cases derived from the spec\",\n};\n\nexport const FIRST_BATCH_ROLES: readonly {\n\treadonly metadata: RoleMetadataV1;\n\treadonly run: FirstBatchRoleV1[\"run\"];\n}[] = [\n\t{\n\t\tmetadata: ARCHITECTURE_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `architecture findings for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n\t{\n\t\tmetadata: IMPLEMENTER_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `patch for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n\t{\n\t\tmetadata: TEST_DESIGNER_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `test cases for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n];\n\n/** Registers and returns the first-batch role vertical slices. */\nexport function createFirstBatchRoleSlices(catalog: RoleCatalogV1): {\n\treadonly roles: readonly FirstBatchRoleV1[];\n\trun(roleId: string, spec: string): Promise<{ readonly roleId: string; readonly result: string }>;\n} {\n\tconst roles = FIRST_BATCH_ROLES.map((role) => ({\n\t\tmetadata: validateRoleMetadataV1(role.metadata),\n\t\trun: role.run,\n\t}));\n\tfor (const role of roles) catalog.register(role.metadata);\n\tconst runners = new Map(roles.map((role) => [role.metadata.roleId, role.run]));\n\treturn {\n\t\troles,\n\t\tasync run(roleId, spec) {\n\t\t\tconst run = runners.get(roleId);\n\t\t\tif (!run) throw new Error(t(\"Unknown first-batch role: {roleId}\", { roleId }));\n\t\t\treturn { roleId, result: (await run({ spec })).result };\n\t\t},\n\t};\n}\n"]}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * First-batch role vertical slices (2.2c): 架构/实现/测试.
3
+ *
4
+ * Each role is a `RoleMetadataV1` registered in the role catalog plus a
5
+ * deterministic runner that only consumes the public facade — no private
6
+ * session access. These are the first vertical slices proving that a role can
7
+ * be declared, discovered, and executed end to end.
8
+ */
9
+ import { t } from "@agent-forge/host/i18n";
10
+ import { validateRoleMetadataV1 } from "./role-catalog.js";
11
+ const ARCHITECTURE_ROLE = {
12
+ roleId: "architecture-analyst",
13
+ title: "Architecture Analyst",
14
+ input: { schemaId: "role/architecture-input@1", schemaVersion: 1 },
15
+ output: { schemaId: "role/architecture-output@1", schemaVersion: 1 },
16
+ capabilityDependencies: [{ capabilityId: "tool.read", capabilityVersion: 1, required: true }],
17
+ writableScopes: ["session"],
18
+ concurrency: "parallel",
19
+ completionCriteria: "module structure findings recorded",
20
+ };
21
+ const IMPLEMENTER_ROLE = {
22
+ roleId: "implementer",
23
+ title: "Implementer",
24
+ input: { schemaId: "role/implement-input@1", schemaVersion: 1 },
25
+ output: { schemaId: "role/implement-output@1", schemaVersion: 1 },
26
+ capabilityDependencies: [
27
+ { capabilityId: "tool.edit", capabilityVersion: 1, required: true },
28
+ { capabilityId: "tool.write", capabilityVersion: 1, required: false },
29
+ ],
30
+ writableScopes: ["session", "cycle"],
31
+ concurrency: "single",
32
+ completionCriteria: "patch produced for the requested spec",
33
+ };
34
+ const TEST_DESIGNER_ROLE = {
35
+ roleId: "test-designer",
36
+ title: "Test Designer",
37
+ input: { schemaId: "role/test-input@1", schemaVersion: 1 },
38
+ output: { schemaId: "role/test-output@1", schemaVersion: 1 },
39
+ capabilityDependencies: [{ capabilityId: "tool.read", capabilityVersion: 1, required: true }],
40
+ writableScopes: ["session"],
41
+ concurrency: "parallel",
42
+ completionCriteria: "test cases derived from the spec",
43
+ };
44
+ export const FIRST_BATCH_ROLES = [
45
+ {
46
+ metadata: ARCHITECTURE_ROLE,
47
+ run: async (input) => ({
48
+ result: `architecture findings for: ${input.spec.slice(0, 64)}`,
49
+ }),
50
+ },
51
+ {
52
+ metadata: IMPLEMENTER_ROLE,
53
+ run: async (input) => ({
54
+ result: `patch for: ${input.spec.slice(0, 64)}`,
55
+ }),
56
+ },
57
+ {
58
+ metadata: TEST_DESIGNER_ROLE,
59
+ run: async (input) => ({
60
+ result: `test cases for: ${input.spec.slice(0, 64)}`,
61
+ }),
62
+ },
63
+ ];
64
+ /** Registers and returns the first-batch role vertical slices. */
65
+ export function createFirstBatchRoleSlices(catalog) {
66
+ const roles = FIRST_BATCH_ROLES.map((role) => ({
67
+ metadata: validateRoleMetadataV1(role.metadata),
68
+ run: role.run,
69
+ }));
70
+ for (const role of roles)
71
+ catalog.register(role.metadata);
72
+ const runners = new Map(roles.map((role) => [role.metadata.roleId, role.run]));
73
+ return {
74
+ roles,
75
+ async run(roleId, spec) {
76
+ const run = runners.get(roleId);
77
+ if (!run)
78
+ throw new Error(t("Unknown first-batch role: {roleId}", { roleId }));
79
+ return { roleId, result: (await run({ spec })).result };
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=first-roles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"first-roles.js","sourceRoot":"","sources":["../src/first-roles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAO3D,MAAM,iBAAiB,GAAmB;IACzC,MAAM,EAAE,sBAAsB;IAC9B,KAAK,EAAE,sBAAsB;IAC7B,KAAK,EAAE,EAAE,QAAQ,EAAE,2BAA2B,EAAE,aAAa,EAAE,CAAC,EAAE;IAClE,MAAM,EAAE,EAAE,QAAQ,EAAE,4BAA4B,EAAE,aAAa,EAAE,CAAC,EAAE;IACpE,sBAAsB,EAAE,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC7F,cAAc,EAAE,CAAC,SAAS,CAAC;IAC3B,WAAW,EAAE,UAAU;IACvB,kBAAkB,EAAE,oCAAoC;CACxD,CAAC;AAEF,MAAM,gBAAgB,GAAmB;IACxC,MAAM,EAAE,aAAa;IACrB,KAAK,EAAE,aAAa;IACpB,KAAK,EAAE,EAAE,QAAQ,EAAE,wBAAwB,EAAE,aAAa,EAAE,CAAC,EAAE;IAC/D,MAAM,EAAE,EAAE,QAAQ,EAAE,yBAAyB,EAAE,aAAa,EAAE,CAAC,EAAE;IACjE,sBAAsB,EAAE;QACvB,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QACnE,EAAE,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE;KACrE;IACD,cAAc,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC;IACpC,WAAW,EAAE,QAAQ;IACrB,kBAAkB,EAAE,uCAAuC;CAC3D,CAAC;AAEF,MAAM,kBAAkB,GAAmB;IAC1C,MAAM,EAAE,eAAe;IACvB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,EAAE,QAAQ,EAAE,mBAAmB,EAAE,aAAa,EAAE,CAAC,EAAE;IAC1D,MAAM,EAAE,EAAE,QAAQ,EAAE,oBAAoB,EAAE,aAAa,EAAE,CAAC,EAAE;IAC5D,sBAAsB,EAAE,CAAC,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC7F,cAAc,EAAE,CAAC,SAAS,CAAC;IAC3B,WAAW,EAAE,UAAU;IACvB,kBAAkB,EAAE,kCAAkC;CACtD,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAGxB;IACL;QACC,QAAQ,EAAE,iBAAiB;QAC3B,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACtB,MAAM,EAAE,8BAA8B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;SAC/D,CAAC;KACF;IACD;QACC,QAAQ,EAAE,gBAAgB;QAC1B,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACtB,MAAM,EAAE,cAAc,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;SAC/C,CAAC;KACF;IACD;QACC,QAAQ,EAAE,kBAAkB;QAC5B,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACtB,MAAM,EAAE,mBAAmB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;SACpD,CAAC;KACF;CACD,CAAC;AAEF,kEAAkE;AAClE,MAAM,UAAU,0BAA0B,CAAC,OAAsB,EAG/D;IACD,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9C,QAAQ,EAAE,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC/C,GAAG,EAAE,IAAI,CAAC,GAAG;KACb,CAAC,CAAC,CAAC;IACJ,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/E,OAAO;QACN,KAAK;QACL,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE;YACvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,oCAAoC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;YAC/E,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAAA,CACxD;KACD,CAAC;AAAA,CACF","sourcesContent":["/**\n * First-batch role vertical slices (2.2c): 架构/实现/测试.\n *\n * Each role is a `RoleMetadataV1` registered in the role catalog plus a\n * deterministic runner that only consumes the public facade — no private\n * session access. These are the first vertical slices proving that a role can\n * be declared, discovered, and executed end to end.\n */\n\nimport { t } from \"@agent-forge/host/i18n\";\nimport type { RoleCatalogV1, RoleMetadataV1 } from \"./role-catalog.ts\";\nimport { validateRoleMetadataV1 } from \"./role-catalog.ts\";\n\nexport interface FirstBatchRoleV1 {\n\treadonly metadata: RoleMetadataV1;\n\treadonly run: (input: { readonly spec: string }) => Promise<{ readonly result: string }>;\n}\n\nconst ARCHITECTURE_ROLE: RoleMetadataV1 = {\n\troleId: \"architecture-analyst\",\n\ttitle: \"Architecture Analyst\",\n\tinput: { schemaId: \"role/architecture-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/architecture-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [{ capabilityId: \"tool.read\", capabilityVersion: 1, required: true }],\n\twritableScopes: [\"session\"],\n\tconcurrency: \"parallel\",\n\tcompletionCriteria: \"module structure findings recorded\",\n};\n\nconst IMPLEMENTER_ROLE: RoleMetadataV1 = {\n\troleId: \"implementer\",\n\ttitle: \"Implementer\",\n\tinput: { schemaId: \"role/implement-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/implement-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [\n\t\t{ capabilityId: \"tool.edit\", capabilityVersion: 1, required: true },\n\t\t{ capabilityId: \"tool.write\", capabilityVersion: 1, required: false },\n\t],\n\twritableScopes: [\"session\", \"cycle\"],\n\tconcurrency: \"single\",\n\tcompletionCriteria: \"patch produced for the requested spec\",\n};\n\nconst TEST_DESIGNER_ROLE: RoleMetadataV1 = {\n\troleId: \"test-designer\",\n\ttitle: \"Test Designer\",\n\tinput: { schemaId: \"role/test-input@1\", schemaVersion: 1 },\n\toutput: { schemaId: \"role/test-output@1\", schemaVersion: 1 },\n\tcapabilityDependencies: [{ capabilityId: \"tool.read\", capabilityVersion: 1, required: true }],\n\twritableScopes: [\"session\"],\n\tconcurrency: \"parallel\",\n\tcompletionCriteria: \"test cases derived from the spec\",\n};\n\nexport const FIRST_BATCH_ROLES: readonly {\n\treadonly metadata: RoleMetadataV1;\n\treadonly run: FirstBatchRoleV1[\"run\"];\n}[] = [\n\t{\n\t\tmetadata: ARCHITECTURE_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `architecture findings for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n\t{\n\t\tmetadata: IMPLEMENTER_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `patch for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n\t{\n\t\tmetadata: TEST_DESIGNER_ROLE,\n\t\trun: async (input) => ({\n\t\t\tresult: `test cases for: ${input.spec.slice(0, 64)}`,\n\t\t}),\n\t},\n];\n\n/** Registers and returns the first-batch role vertical slices. */\nexport function createFirstBatchRoleSlices(catalog: RoleCatalogV1): {\n\treadonly roles: readonly FirstBatchRoleV1[];\n\trun(roleId: string, spec: string): Promise<{ readonly roleId: string; readonly result: string }>;\n} {\n\tconst roles = FIRST_BATCH_ROLES.map((role) => ({\n\t\tmetadata: validateRoleMetadataV1(role.metadata),\n\t\trun: role.run,\n\t}));\n\tfor (const role of roles) catalog.register(role.metadata);\n\tconst runners = new Map(roles.map((role) => [role.metadata.roleId, role.run]));\n\treturn {\n\t\troles,\n\t\tasync run(roleId, spec) {\n\t\t\tconst run = runners.get(roleId);\n\t\t\tif (!run) throw new Error(t(\"Unknown first-batch role: {roleId}\", { roleId }));\n\t\t\treturn { roleId, result: (await run({ spec })).result };\n\t\t},\n\t};\n}\n"]}