@davidorex/pi-agent-dispatch 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +37 -0
  3. package/dist/attested-commit.d.ts +32 -0
  4. package/dist/attested-commit.d.ts.map +1 -0
  5. package/dist/attested-commit.js +61 -0
  6. package/dist/attested-commit.js.map +1 -0
  7. package/dist/auth-gate.d.ts +92 -0
  8. package/dist/auth-gate.d.ts.map +1 -0
  9. package/dist/auth-gate.js +210 -0
  10. package/dist/auth-gate.js.map +1 -0
  11. package/dist/author-agent-spec-tool.d.ts +33 -0
  12. package/dist/author-agent-spec-tool.d.ts.map +1 -0
  13. package/dist/author-agent-spec-tool.js +98 -0
  14. package/dist/author-agent-spec-tool.js.map +1 -0
  15. package/dist/author-tool-grant-tool.d.ts +47 -0
  16. package/dist/author-tool-grant-tool.d.ts.map +1 -0
  17. package/dist/author-tool-grant-tool.js +87 -0
  18. package/dist/author-tool-grant-tool.js.map +1 -0
  19. package/dist/call-agent-tool.d.ts +42 -0
  20. package/dist/call-agent-tool.d.ts.map +1 -0
  21. package/dist/call-agent-tool.js +90 -0
  22. package/dist/call-agent-tool.js.map +1 -0
  23. package/dist/capability-composer.d.ts +11 -0
  24. package/dist/capability-composer.d.ts.map +1 -0
  25. package/dist/capability-composer.js +35 -0
  26. package/dist/capability-composer.js.map +1 -0
  27. package/dist/commit-attested-tool.d.ts +29 -0
  28. package/dist/commit-attested-tool.d.ts.map +1 -0
  29. package/dist/commit-attested-tool.js +45 -0
  30. package/dist/commit-attested-tool.js.map +1 -0
  31. package/dist/composite-loader.d.ts +36 -0
  32. package/dist/composite-loader.d.ts.map +1 -0
  33. package/dist/composite-loader.js +137 -0
  34. package/dist/composite-loader.js.map +1 -0
  35. package/dist/composites/command-allowlist.d.ts +29 -0
  36. package/dist/composites/command-allowlist.d.ts.map +1 -0
  37. package/dist/composites/command-allowlist.js +36 -0
  38. package/dist/composites/command-allowlist.js.map +1 -0
  39. package/dist/composites/git-log.d.ts +31 -0
  40. package/dist/composites/git-log.d.ts.map +1 -0
  41. package/dist/composites/git-log.js +39 -0
  42. package/dist/composites/git-log.js.map +1 -0
  43. package/dist/composites/grep-paths.d.ts +26 -0
  44. package/dist/composites/grep-paths.d.ts.map +1 -0
  45. package/dist/composites/grep-paths.js +34 -0
  46. package/dist/composites/grep-paths.js.map +1 -0
  47. package/dist/composites/read-files.d.ts +24 -0
  48. package/dist/composites/read-files.d.ts.map +1 -0
  49. package/dist/composites/read-files.js +35 -0
  50. package/dist/composites/read-files.js.map +1 -0
  51. package/dist/index.d.ts +18 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +77 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/operation-vocab.d.ts +25 -0
  56. package/dist/operation-vocab.d.ts.map +1 -0
  57. package/dist/operation-vocab.js +78 -0
  58. package/dist/operation-vocab.js.map +1 -0
  59. package/dist/read-truncation-gate.d.ts +143 -0
  60. package/dist/read-truncation-gate.d.ts.map +1 -0
  61. package/dist/read-truncation-gate.js +175 -0
  62. package/dist/read-truncation-gate.js.map +1 -0
  63. package/dist/real-check-runner.d.ts +66 -0
  64. package/dist/real-check-runner.d.ts.map +1 -0
  65. package/dist/real-check-runner.js +133 -0
  66. package/dist/real-check-runner.js.map +1 -0
  67. package/dist/run-real-checks-tool.d.ts +28 -0
  68. package/dist/run-real-checks-tool.d.ts.map +1 -0
  69. package/dist/run-real-checks-tool.js +47 -0
  70. package/dist/run-real-checks-tool.js.map +1 -0
  71. package/dist/run-work-order-loop-tool.d.ts +35 -0
  72. package/dist/run-work-order-loop-tool.d.ts.map +1 -0
  73. package/dist/run-work-order-loop-tool.js +46 -0
  74. package/dist/run-work-order-loop-tool.js.map +1 -0
  75. package/dist/verified-identity.d.ts +54 -0
  76. package/dist/verified-identity.d.ts.map +1 -0
  77. package/dist/verified-identity.js +133 -0
  78. package/dist/verified-identity.js.map +1 -0
  79. package/dist/work-order-loop.d.ts +82 -0
  80. package/dist/work-order-loop.d.ts.map +1 -0
  81. package/dist/work-order-loop.js +149 -0
  82. package/dist/work-order-loop.js.map +1 -0
  83. package/package.json +59 -0
  84. package/skill-narrative.md +53 -0
  85. package/skills/pi-agent-dispatch/SKILL.md +138 -0
@@ -0,0 +1,47 @@
1
+ /**
2
+ * author-tool-grant Pi tool — writes config.tool_operations[] /
3
+ * config.tool_operations_forbidden[] entries. Capability authoring is
4
+ * human-authorized via the auth-gate confirm at the pi-dispatch layer:
5
+ * the agent may issue the call, the operator authorizes at the terminal,
6
+ * and the auth-gate stamps event.input.writer with the verified terminal-
7
+ * operator identity before the body runs.
8
+ *
9
+ * Two-arm target: `tool_operations` (the grant registry) or
10
+ * `tool_operations_forbidden` (the L5 project-forbidden list). Both arms
11
+ * refuse FORBIDDEN_WHOLESALE_OPERATIONS L1 tokens — for `tool_operations`
12
+ * we refuse entries whose canonical_id IS a wholesale token (the canon
13
+ * we're protecting); for `tool_operations_forbidden` we refuse keys
14
+ * already in L1 (no-op + clarity — they're framework-forbidden so
15
+ * adding them to L5 is redundant).
16
+ *
17
+ * Dispatches to amendConfigEntry from pi-context.
18
+ */
19
+ import { Type } from "@earendil-works/pi-ai";
20
+ import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@earendil-works/pi-coding-agent";
21
+ export declare const authorToolGrantTool: {
22
+ name: string;
23
+ label: string;
24
+ description: string;
25
+ promptSnippet: string;
26
+ parameters: Type.TObject<{
27
+ target: Type.TUnion<[Type.TLiteral<"tool_operations">, Type.TLiteral<"tool_operations_forbidden">]>;
28
+ operation: Type.TUnion<[Type.TLiteral<"add">, Type.TLiteral<"remove">]>;
29
+ key: Type.TString;
30
+ entry: Type.TOptional<Type.TUnknown>;
31
+ writer: Type.TObject<{
32
+ kind: Type.TString;
33
+ user: Type.TString;
34
+ }>;
35
+ }>;
36
+ execute(_toolCallId: string, params: {
37
+ target: "tool_operations" | "tool_operations_forbidden";
38
+ operation: "add" | "remove";
39
+ key: string;
40
+ entry?: unknown;
41
+ writer: {
42
+ kind: string;
43
+ user: string;
44
+ };
45
+ }, _signal: AbortSignal, _onUpdate: AgentToolUpdateCallback, ctx: ExtensionContext): Promise<AgentToolResult<undefined>>;
46
+ };
47
+ //# sourceMappingURL=author-tool-grant-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-tool-grant-tool.d.ts","sourceRoot":"","sources":["../src/author-tool-grant-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGlH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;yBAwCjB,MAAM,UACX;QACP,MAAM,EAAE,iBAAiB,GAAG,2BAA2B,CAAC;QACxD,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAC;QAC5B,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;KACvC,WACQ,WAAW,aACT,uBAAuB,OAC7B,gBAAgB,GACnB,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;CAmDtC,CAAC"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * author-tool-grant Pi tool — writes config.tool_operations[] /
3
+ * config.tool_operations_forbidden[] entries. Capability authoring is
4
+ * human-authorized via the auth-gate confirm at the pi-dispatch layer:
5
+ * the agent may issue the call, the operator authorizes at the terminal,
6
+ * and the auth-gate stamps event.input.writer with the verified terminal-
7
+ * operator identity before the body runs.
8
+ *
9
+ * Two-arm target: `tool_operations` (the grant registry) or
10
+ * `tool_operations_forbidden` (the L5 project-forbidden list). Both arms
11
+ * refuse FORBIDDEN_WHOLESALE_OPERATIONS L1 tokens — for `tool_operations`
12
+ * we refuse entries whose canonical_id IS a wholesale token (the canon
13
+ * we're protecting); for `tool_operations_forbidden` we refuse keys
14
+ * already in L1 (no-op + clarity — they're framework-forbidden so
15
+ * adding them to L5 is redundant).
16
+ *
17
+ * Dispatches to amendConfigEntry from pi-context.
18
+ */
19
+ import { amendConfigEntry } from "@davidorex/pi-context/context";
20
+ import { Type } from "@earendil-works/pi-ai";
21
+ import { FORBIDDEN_WHOLESALE_OPERATIONS } from "./operation-vocab.js";
22
+ export const authorToolGrantTool = {
23
+ name: "author-tool-grant",
24
+ label: "Author Tool Grant",
25
+ description: "Add or remove an entry in config.tool_operations[] or config.tool_operations_forbidden[]. Requires user authorization via interactive confirmation at the pi-dispatch auth-gate; on confirm, the verified terminal-operator identity is stamped as writer. Refuses any attempt to register a framework-forbidden wholesale token.",
26
+ promptSnippet: "Author a config tool-grant entry (operation registration or project-forbidden token).",
27
+ parameters: Type.Object({
28
+ target: Type.Union([Type.Literal("tool_operations"), Type.Literal("tool_operations_forbidden")], {
29
+ description: "Which config registry to mutate.",
30
+ }),
31
+ operation: Type.Union([Type.Literal("add"), Type.Literal("remove")], {
32
+ description: "amendConfigEntry operation.",
33
+ }),
34
+ key: Type.String({
35
+ description: "For tool_operations: the canonical_id (must match entry.canonical_id). For tool_operations_forbidden: the token string.",
36
+ }),
37
+ entry: Type.Optional(Type.Unknown({
38
+ description: "ToolOperationDecl object — required for target=tool_operations + operation=add.",
39
+ })),
40
+ writer: Type.Object({
41
+ kind: Type.String({
42
+ description: "Writer kind discriminator (human / agent / monitor / workflow). The pi-dispatch auth-gate overrides this with 'human' on confirm when a verified terminal-operator identity is discoverable.",
43
+ }),
44
+ user: Type.String({
45
+ description: "Writer identity (e.g. 'davidryan@gmail.com'). Overwritten by the auth-gate with the verified terminal-operator identity when one is discoverable on confirm=true.",
46
+ }),
47
+ }, {
48
+ description: "DispatchContext.writer payload; see pi-context/src/dispatch-context.ts for the discriminated union.",
49
+ }),
50
+ }),
51
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
52
+ // Identity check has moved to the pi-dispatch auth-gate
53
+ // (pi.on('tool_call') handler in this same package). By the time
54
+ // the execute body runs, the auth-gate has already prompted the
55
+ // operator and — on confirm=true with a verifiable identity —
56
+ // stamped event.input.writer with the verified terminal-operator
57
+ // identity. The body trusts the writer field as-is.
58
+ if (!params.writer?.user) {
59
+ throw new Error("author-tool-grant: writer.user is required.");
60
+ }
61
+ const forbidden = FORBIDDEN_WHOLESALE_OPERATIONS;
62
+ if (params.target === "tool_operations" && params.operation === "add") {
63
+ const entryObj = (params.entry ?? {});
64
+ const canonicalId = entryObj.canonical_id ?? params.key;
65
+ if (forbidden.includes(canonicalId)) {
66
+ throw new Error(`author-tool-grant: refusing to register forbidden wholesale token '${canonicalId}' in tool_operations (L1 framework canon; source change + release required to alter).`);
67
+ }
68
+ }
69
+ if (params.target === "tool_operations_forbidden" && forbidden.includes(params.key)) {
70
+ throw new Error(`author-tool-grant: token '${params.key}' is already in L1 framework FORBIDDEN_WHOLESALE_OPERATIONS — adding to L5 project list is redundant.`);
71
+ }
72
+ const dispatchCtx = {
73
+ writer: { kind: "human", user: params.writer.user },
74
+ };
75
+ amendConfigEntry(ctx.cwd, params.target, params.operation, params.key, params.entry, dispatchCtx);
76
+ return {
77
+ details: undefined,
78
+ content: [
79
+ {
80
+ type: "text",
81
+ text: `${params.operation} ${params.target}[${params.key}] (writer=human:${params.writer.user})`,
82
+ },
83
+ ],
84
+ };
85
+ },
86
+ };
87
+ //# sourceMappingURL=author-tool-grant-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-tool-grant-tool.js","sourceRoot":"","sources":["../src/author-tool-grant-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AAEtE,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAClC,IAAI,EAAE,mBAAmB;IACzB,KAAK,EAAE,mBAAmB;IAC1B,WAAW,EACV,mUAAmU;IACpU,aAAa,EAAE,uFAAuF;IACtG,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACvB,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC,EAAE;YAChG,WAAW,EAAE,kCAAkC;SAC/C,CAAC;QACF,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE;YACpE,WAAW,EAAE,6BAA6B;SAC1C,CAAC;QACF,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC;YAChB,WAAW,EACV,yHAAyH;SAC1H,CAAC;QACF,KAAK,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,OAAO,CAAC;YACZ,WAAW,EAAE,iFAAiF;SAC9F,CAAC,CACF;QACD,MAAM,EAAE,IAAI,CAAC,MAAM,CAClB;YACC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBACjB,WAAW,EACV,8LAA8L;aAC/L,CAAC;YACF,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;gBACjB,WAAW,EACV,mKAAmK;aACpK,CAAC;SACF,EACD;YACC,WAAW,EACV,qGAAqG;SACtG,CACD;KACD,CAAC;IACF,KAAK,CAAC,OAAO,CACZ,WAAmB,EACnB,MAMC,EACD,OAAoB,EACpB,SAAkC,EAClC,GAAqB;QAErB,wDAAwD;QACxD,iEAAiE;QACjE,gEAAgE;QAChE,8DAA8D;QAC9D,iEAAiE;QACjE,oDAAoD;QACpD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,SAAS,GAAG,8BAAmD,CAAC;QAEtE,IAAI,MAAM,CAAC,MAAM,KAAK,iBAAiB,IAAI,MAAM,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;YACvE,MAAM,QAAQ,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAA8B,CAAC;YACnE,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,IAAI,MAAM,CAAC,GAAG,CAAC;YACxD,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBACrC,MAAM,IAAI,KAAK,CACd,sEAAsE,WAAW,uFAAuF,CACxK,CAAC;YACH,CAAC;QACF,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,KAAK,2BAA2B,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;YACrF,MAAM,IAAI,KAAK,CACd,6BAA6B,MAAM,CAAC,GAAG,uGAAuG,CAC9I,CAAC;QACH,CAAC;QAED,MAAM,WAAW,GAAoB;YACpC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;SACnD,CAAC;QACF,gBAAgB,CACf,GAAG,CAAC,GAAG,EACP,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,GAAG,EACV,MAAM,CAAC,KAA4C,EACnD,WAAW,CACX,CAAC;QAEF,OAAO;YACN,OAAO,EAAE,SAAS;YAClB,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,GAAG,mBAAmB,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG;iBAChG;aACD;SACD,CAAC;IACH,CAAC;CACD,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * call-agent Pi tool — the in-pi sub-agent agent-as-tool registration site
3
+ * (FEAT-004, per narrowed DEC-0044). Loads spec via jit-agents library,
4
+ * compiles with input + ctx, composes grant (parentGrant ∩ requestedGrant
5
+ * per FEAT-005), invokes executeAgent (TASK-081 clamp enforces at
6
+ * dispatch boundary). Returns the typed result.
7
+ */
8
+ import type { CompiledAgent, DispatchContext, JitAgentResult } from "@davidorex/pi-jit-agents/types";
9
+ import { Type } from "@earendil-works/pi-ai";
10
+ import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@earendil-works/pi-coding-agent";
11
+ /**
12
+ * Internal indirection for test-time interception of the executeAgent
13
+ * dispatch. Production code path never reassigns this. Tests swap via
14
+ * `_internals.executeAgent = (...) => mockResult` and restore after.
15
+ * This is a deliberate seam — restructuring the tool to thread a
16
+ * dependency through every call site would invert the public API for
17
+ * a test-only need.
18
+ */
19
+ export declare const _internals: {
20
+ executeAgent: (c: CompiledAgent, d: DispatchContext) => Promise<JitAgentResult>;
21
+ };
22
+ export declare const callAgentTool: {
23
+ name: string;
24
+ label: string;
25
+ description: string;
26
+ promptSnippet: string;
27
+ parameters: Type.TObject<{
28
+ spec_name: Type.TString;
29
+ input: Type.TUnknown;
30
+ parent_grant: Type.TOptional<Type.TArray<Type.TString>>;
31
+ requested_grant: Type.TOptional<Type.TArray<Type.TString>>;
32
+ max_tokens: Type.TOptional<Type.TNumber>;
33
+ }>;
34
+ execute(_toolCallId: string, params: {
35
+ spec_name: string;
36
+ input: unknown;
37
+ parent_grant?: string[];
38
+ requested_grant?: string[];
39
+ max_tokens?: number;
40
+ }, signal: AbortSignal, _onUpdate: AgentToolUpdateCallback, ctx: ExtensionContext): Promise<AgentToolResult<JitAgentResult>>;
41
+ };
42
+ //# sourceMappingURL=call-agent-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-agent-tool.d.ts","sourceRoot":"","sources":["../src/call-agent-tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAErG,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AASlH;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,EAAE;IAAE,YAAY,EAAE,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,cAAc,CAAC,CAAA;CAEzG,CAAC;AAEF,eAAO,MAAM,aAAa;;;;;;;;;;;;yBAuBX,MAAM,UACX;QACP,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,OAAO,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,UACO,WAAW,aACR,uBAAuB,OAC7B,gBAAgB,GACnB,OAAO,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;CAgD3C,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * call-agent Pi tool — the in-pi sub-agent agent-as-tool registration site
3
+ * (FEAT-004, per narrowed DEC-0044). Loads spec via jit-agents library,
4
+ * compiles with input + ctx, composes grant (parentGrant ∩ requestedGrant
5
+ * per FEAT-005), invokes executeAgent (TASK-081 clamp enforces at
6
+ * dispatch boundary). Returns the typed result.
7
+ */
8
+ import { createAgentLoader } from "@davidorex/pi-jit-agents/agent-spec";
9
+ import { compileAgent } from "@davidorex/pi-jit-agents/compile";
10
+ import { executeAgent as canonicalExecuteAgent } from "@davidorex/pi-jit-agents/runtime";
11
+ import { createTemplateEnv } from "@davidorex/pi-jit-agents/template";
12
+ import { Type } from "@earendil-works/pi-ai";
13
+ import { composeToolGrant } from "./capability-composer.js";
14
+ function parseModelSpec(spec) {
15
+ const slashIndex = spec.indexOf("/");
16
+ if (slashIndex !== -1)
17
+ return { provider: spec.slice(0, slashIndex), modelId: spec.slice(slashIndex + 1) };
18
+ return { provider: "anthropic", modelId: spec };
19
+ }
20
+ /**
21
+ * Internal indirection for test-time interception of the executeAgent
22
+ * dispatch. Production code path never reassigns this. Tests swap via
23
+ * `_internals.executeAgent = (...) => mockResult` and restore after.
24
+ * This is a deliberate seam — restructuring the tool to thread a
25
+ * dependency through every call site would invert the public API for
26
+ * a test-only need.
27
+ */
28
+ export const _internals = {
29
+ executeAgent: canonicalExecuteAgent,
30
+ };
31
+ export const callAgentTool = {
32
+ name: "call-agent",
33
+ label: "Call Agent",
34
+ description: "Dispatch a privileged JIT-agent as a typed tool call. Loads the named .agent.yaml, compiles with input, composes the tool grant (intersection of caller's parentGrant and the agent's requestedGrant), and executes via pi-jit-agents executeAgent (clamp enforces child ⊆ parent at dispatch boundary).",
35
+ promptSnippet: "Dispatch a typed sub-agent with scoped capability grant.",
36
+ parameters: Type.Object({
37
+ spec_name: Type.String({
38
+ description: "Name of the agent spec to load (resolves to <name>.agent.yaml in the agents tier).",
39
+ }),
40
+ input: Type.Unknown({ description: "Typed input passed to the agent's compileAgent context." }),
41
+ parent_grant: Type.Optional(Type.Array(Type.String(), { description: "The caller's own tool grant. Default-empty." })),
42
+ requested_grant: Type.Optional(Type.Array(Type.String(), {
43
+ description: "The grant requested for the dispatched sub-agent. Will be clamped to the intersection with parent_grant.",
44
+ })),
45
+ max_tokens: Type.Optional(Type.Number({ description: "Max tokens for the LLM call. Defaults to 1024." })),
46
+ }),
47
+ async execute(_toolCallId, params, signal, _onUpdate, ctx) {
48
+ // 1. Load spec via jit-agents canonical loader
49
+ const loadAgent = createAgentLoader({ cwd: ctx.cwd });
50
+ const spec = loadAgent(params.spec_name);
51
+ // 2. Compile spec with input
52
+ const env = createTemplateEnv({ cwd: ctx.cwd });
53
+ const compiled = compileAgent(spec, { env, input: params.input, cwd: ctx.cwd });
54
+ // 3. Resolve model + auth via ExtensionContext.modelRegistry
55
+ const modelSpec = compiled.model ?? spec.model;
56
+ if (!modelSpec) {
57
+ throw new Error(`call-agent: agent '${params.spec_name}' has no model specified.`);
58
+ }
59
+ const { provider, modelId } = parseModelSpec(modelSpec);
60
+ const model = ctx.modelRegistry.find(provider, modelId);
61
+ if (!model) {
62
+ throw new Error(`call-agent: model '${modelSpec}' not found in modelRegistry for agent '${params.spec_name}'.`);
63
+ }
64
+ const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
65
+ if (!auth.ok) {
66
+ throw new Error(`call-agent: auth resolution failed for '${modelSpec}': ${auth.error}`);
67
+ }
68
+ // 4. Compose grant (FEAT-005): intersect parent_grant ∩ requested_grant
69
+ const composedGrant = composeToolGrant(params.parent_grant, params.requested_grant);
70
+ // 5. Build DispatchContext + dispatch (TASK-081 clamp enforces at executeAgent boundary)
71
+ const dispatch = {
72
+ model: model,
73
+ auth: { apiKey: auth.apiKey ?? "", headers: auth.headers ?? {} },
74
+ parentGrant: composedGrant,
75
+ maxTokens: params.max_tokens ?? 1024,
76
+ signal,
77
+ };
78
+ const result = await _internals.executeAgent(compiled, dispatch);
79
+ return {
80
+ details: result,
81
+ content: [
82
+ {
83
+ type: "text",
84
+ text: `Dispatched agent '${params.spec_name}' (grant=[${composedGrant.join(", ")}]); result.output type=${typeof result.output}`,
85
+ },
86
+ ],
87
+ };
88
+ },
89
+ };
90
+ //# sourceMappingURL=call-agent-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-agent-tool.js","sourceRoot":"","sources":["../src/call-agent-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Capability composition (FEAT-005). Composes a child grant from a parent
3
+ * grant and a requested grant by intersection — DEC-0047 clamp semantics.
4
+ * The TASK-081 executeAgent boundary enforces compiled.tools ⊆ parentGrant;
5
+ * composeToolGrant prepares the parentGrant passed to that boundary so the
6
+ * child can never exceed what the parent itself was granted.
7
+ */
8
+ import { type OperationDescriptor } from "./operation-vocab.js";
9
+ export declare function composeToolGrant(parentGrant: string[] | undefined, requestedGrant: string[] | undefined): string[];
10
+ export declare function resolveOperationVocabulary(cwd: string): Record<string, OperationDescriptor>;
11
+ //# sourceMappingURL=capability-composer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-composer.d.ts","sourceRoot":"","sources":["../src/capability-composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,sBAAsB,CAAC;AAEzF,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,CAIlH;AAED,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAa3F"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Capability composition (FEAT-005). Composes a child grant from a parent
3
+ * grant and a requested grant by intersection — DEC-0047 clamp semantics.
4
+ * The TASK-081 executeAgent boundary enforces compiled.tools ⊆ parentGrant;
5
+ * composeToolGrant prepares the parentGrant passed to that boundary so the
6
+ * child can never exceed what the parent itself was granted.
7
+ */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { tryResolveContextDir } from "@davidorex/pi-context/context-dir";
11
+ import { TOOL_OPERATION_DEFAULTS } from "./operation-vocab.js";
12
+ export function composeToolGrant(parentGrant, requestedGrant) {
13
+ const parent = new Set(parentGrant ?? []);
14
+ const requested = requestedGrant ?? [];
15
+ return requested.filter((op) => parent.has(op));
16
+ }
17
+ export function resolveOperationVocabulary(cwd) {
18
+ const root = tryResolveContextDir(cwd);
19
+ if (root === null)
20
+ return { ...TOOL_OPERATION_DEFAULTS };
21
+ const configPath = path.join(root, "config.json");
22
+ if (!fs.existsSync(configPath))
23
+ return { ...TOOL_OPERATION_DEFAULTS };
24
+ try {
25
+ const cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
26
+ const overrides = {};
27
+ for (const entry of cfg.tool_operations ?? [])
28
+ overrides[entry.canonical_id] = entry;
29
+ return { ...TOOL_OPERATION_DEFAULTS, ...overrides };
30
+ }
31
+ catch {
32
+ return { ...TOOL_OPERATION_DEFAULTS };
33
+ }
34
+ }
35
+ //# sourceMappingURL=capability-composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability-composer.js","sourceRoot":"","sources":["../src/capability-composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,EAA4B,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAEzF,MAAM,UAAU,gBAAgB,CAAC,WAAiC,EAAE,cAAoC;IACvG,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,SAAS,GAAG,cAAc,IAAI,EAAE,CAAC;IACvC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACrD,MAAM,IAAI,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,GAAG,uBAAuB,EAAE,CAAC;IACzD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAClD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,GAAG,uBAAuB,EAAE,CAAC;IACtE,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAgD,CAAC;QAC3G,MAAM,SAAS,GAAwC,EAAE,CAAC;QAC1D,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,eAAe,IAAI,EAAE;YAAE,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC;QACrF,OAAO,EAAE,GAAG,uBAAuB,EAAE,GAAG,SAAS,EAAE,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,GAAG,uBAAuB,EAAE,CAAC;IACvC,CAAC;AACF,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * commit-attested Pi tool — stages declared files + invokes git commit
3
+ * with a writer.kind=agent attestation footer (DEC-0047). Husky pre-commit
4
+ * runs as the backup gate; never bypass (--no-verify is forbidden per
5
+ * feedback_no_destructive_git_ops). The primary verification gate is
6
+ * run-real-checks (TASK-090) called BEFORE this tool.
7
+ */
8
+ import { Type } from "@earendil-works/pi-ai";
9
+ import type { AgentToolResult, AgentToolUpdateCallback, ExtensionContext } from "@earendil-works/pi-coding-agent";
10
+ import { type AttestedCommitResult } from "./attested-commit.js";
11
+ export declare const commitAttestedTool: {
12
+ name: string;
13
+ label: string;
14
+ description: string;
15
+ promptSnippet: string;
16
+ parameters: Type.TObject<{
17
+ files: Type.TArray<Type.TString>;
18
+ message: Type.TString;
19
+ agent_id: Type.TString;
20
+ work_order_id: Type.TOptional<Type.TString>;
21
+ }>;
22
+ execute(_toolCallId: string, params: {
23
+ files: string[];
24
+ message: string;
25
+ agent_id: string;
26
+ work_order_id?: string;
27
+ }, _signal: AbortSignal, _onUpdate: AgentToolUpdateCallback, ctx: ExtensionContext): Promise<AgentToolResult<AttestedCommitResult>>;
28
+ };
29
+ //# sourceMappingURL=commit-attested-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-attested-tool.d.ts","sourceRoot":"","sources":["../src/commit-attested-tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAClH,OAAO,EAAE,KAAK,oBAAoB,EAAkB,MAAM,sBAAsB,CAAC;AAEjF,eAAO,MAAM,kBAAkB;;;;;;;;;;;yBAiBhB,MAAM,UACX;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,WAC7E,WAAW,aACT,uBAAuB,OAC7B,gBAAgB,GACnB,OAAO,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC;CAoBjD,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * commit-attested Pi tool — stages declared files + invokes git commit
3
+ * with a writer.kind=agent attestation footer (DEC-0047). Husky pre-commit
4
+ * runs as the backup gate; never bypass (--no-verify is forbidden per
5
+ * feedback_no_destructive_git_ops). The primary verification gate is
6
+ * run-real-checks (TASK-090) called BEFORE this tool.
7
+ */
8
+ import { Type } from "@earendil-works/pi-ai";
9
+ import { attestedCommit } from "./attested-commit.js";
10
+ export const commitAttestedTool = {
11
+ name: "commit-attested",
12
+ label: "Commit Attested",
13
+ description: "Stage declared files + invoke git commit with DispatchContext writer.kind=agent attestation footer. Husky pre-commit runs as backup gate; never bypass (--no-verify forbidden per feedback_no_destructive_git_ops). The primary gate is run-real-checks called BEFORE this tool.",
14
+ promptSnippet: "Commit agent-authored work-product files with attestation footer.",
15
+ parameters: Type.Object({
16
+ files: Type.Array(Type.String(), { description: "Files to stage + commit. Empty array refused." }),
17
+ message: Type.String({
18
+ description: "Commit message body (the attestation footer is appended automatically).",
19
+ }),
20
+ agent_id: Type.String({
21
+ description: "Agent id for writer.kind=agent attestation (e.g. 'spec-implementer-001').",
22
+ }),
23
+ work_order_id: Type.Optional(Type.String({ description: "Optional work-order id for the attestation footer." })),
24
+ }),
25
+ async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
26
+ const result = await attestedCommit(ctx.cwd, {
27
+ files: params.files,
28
+ message: params.message,
29
+ agent_id: params.agent_id,
30
+ work_order_id: params.work_order_id,
31
+ });
32
+ return {
33
+ details: result,
34
+ content: [
35
+ {
36
+ type: "text",
37
+ text: result.committed
38
+ ? `Committed ${result.commit_sha} (agent/${params.agent_id})`
39
+ : `Commit FAILED (exit ${result.exit_code}): ${result.stderr.slice(0, 500)}`,
40
+ },
41
+ ],
42
+ };
43
+ },
44
+ };
45
+ //# sourceMappingURL=commit-attested-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit-attested-tool.js","sourceRoot":"","sources":["../src/commit-attested-tool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,OAAO,EAA6B,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEjF,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,iBAAiB;IACxB,WAAW,EACV,kRAAkR;IACnR,aAAa,EAAE,mEAAmE;IAClF,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC;QAClG,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC;YACpB,WAAW,EAAE,yEAAyE;SACtF,CAAC;QACF,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;YACrB,WAAW,EAAE,2EAA2E;SACxF,CAAC;QACF,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAC;KAChH,CAAC;IACF,KAAK,CAAC,OAAO,CACZ,WAAmB,EACnB,MAAsF,EACtF,OAAoB,EACpB,SAAkC,EAClC,GAAqB;QAErB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE;YAC5C,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,aAAa,EAAE,MAAM,CAAC,aAAa;SACnC,CAAC,CAAC;QAEH,OAAO;YACN,OAAO,EAAE,MAAM;YACf,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM,CAAC,SAAS;wBACrB,CAAC,CAAC,aAAa,MAAM,CAAC,UAAU,WAAW,MAAM,CAAC,QAAQ,GAAG;wBAC7D,CAAC,CAAC,uBAAuB,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;iBAC7E;aACD;SACD,CAAC;IACH,CAAC;CACD,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * composite-loader — dynamic per-instance Pi tool registration from
3
+ * config.tool_operations[] entries declaring a KIND (FEAT-010 Hybrid 3 v2).
4
+ *
5
+ * Reads config via loadContext(cwd). For each entry with `kind` set:
6
+ * 1. Reject if canonical_id is in the forbidden union (L1 framework list ∪
7
+ * L5 project list config.tool_operations_forbidden[]) — throw, do not
8
+ * register.
9
+ * 2. Skip (with `skipped` return entry) if kind is unknown to KIND_REGISTRY
10
+ * — forward compatibility allows future KIND additions in newer dispatch
11
+ * versions without crashing older config.
12
+ * 3. Otherwise: closure-bind instance_params + register a Pi tool named
13
+ * canonical_id whose parameters match the KIND's argsSchema. The runtime
14
+ * callsite supplies args only; instance scope is fixed at this
15
+ * registration point.
16
+ *
17
+ * Entries without `kind` are FEAT-005 static-tool references and skipped here
18
+ * (not the composite path).
19
+ */
20
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
21
+ export interface LoadCompositesResult {
22
+ registered: string[];
23
+ skipped: {
24
+ canonical_id: string;
25
+ reason: string;
26
+ }[];
27
+ /**
28
+ * True when loadContext returned config=null (no .pi-context.json pointer
29
+ * or no config.json). Lets the extension factory caller surface the
30
+ * absence via pi.ui.notify when available — independent of the
31
+ * extension_load_warning TraceEntry already emitted from this loader.
32
+ */
33
+ config_absent: boolean;
34
+ }
35
+ export declare function loadComposites(cwd: string, pi: ExtensionAPI): LoadCompositesResult;
36
+ //# sourceMappingURL=composite-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-loader.d.ts","sourceRoot":"","sources":["../src/composite-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAqDpE,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpD;;;;;OAKG;IACH,aAAa,EAAE,OAAO,CAAC;CACvB;AAsDD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,oBAAoB,CAqDlF"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * composite-loader — dynamic per-instance Pi tool registration from
3
+ * config.tool_operations[] entries declaring a KIND (FEAT-010 Hybrid 3 v2).
4
+ *
5
+ * Reads config via loadContext(cwd). For each entry with `kind` set:
6
+ * 1. Reject if canonical_id is in the forbidden union (L1 framework list ∪
7
+ * L5 project list config.tool_operations_forbidden[]) — throw, do not
8
+ * register.
9
+ * 2. Skip (with `skipped` return entry) if kind is unknown to KIND_REGISTRY
10
+ * — forward compatibility allows future KIND additions in newer dispatch
11
+ * versions without crashing older config.
12
+ * 3. Otherwise: closure-bind instance_params + register a Pi tool named
13
+ * canonical_id whose parameters match the KIND's argsSchema. The runtime
14
+ * callsite supplies args only; instance scope is fixed at this
15
+ * registration point.
16
+ *
17
+ * Entries without `kind` are FEAT-005 static-tool references and skipped here
18
+ * (not the composite path).
19
+ */
20
+ import { randomUUID } from "node:crypto";
21
+ import { homedir } from "node:os";
22
+ import path from "node:path";
23
+ import { loadContext } from "@davidorex/pi-context/context";
24
+ import { writeAgentTrace } from "@davidorex/pi-jit-agents";
25
+ import { commandAllowlistArgsSchema, runCommandAllowlist, } from "./composites/command-allowlist.js";
26
+ import { gitLogArgsSchema, runGitLog } from "./composites/git-log.js";
27
+ import { grepPathsArgsSchema, runGrepPaths, } from "./composites/grep-paths.js";
28
+ import { readFilesArgsSchema, runReadFiles, } from "./composites/read-files.js";
29
+ import { FORBIDDEN_WHOLESALE_OPERATIONS } from "./operation-vocab.js";
30
+ const KIND_REGISTRY = {
31
+ "read-files": {
32
+ argsSchema: readFilesArgsSchema,
33
+ run: (cwd, instance, args) => runReadFiles(cwd, instance, args),
34
+ },
35
+ "git-log": {
36
+ argsSchema: gitLogArgsSchema,
37
+ run: (cwd, instance, args) => runGitLog(cwd, instance, args),
38
+ },
39
+ "grep-paths": {
40
+ argsSchema: grepPathsArgsSchema,
41
+ run: (cwd, instance, args) => runGrepPaths(cwd, instance, args),
42
+ },
43
+ "command-allowlist": {
44
+ argsSchema: commandAllowlistArgsSchema,
45
+ run: (cwd, instance, args) => runCommandAllowlist(cwd, instance, args),
46
+ },
47
+ };
48
+ /**
49
+ * ULID-shape filler — the canonical writeAgentTrace path expects a
50
+ * 26-character Crockford-base32 ULID per the schema. Until a real ULID
51
+ * dependency is added (the rest of pi-jit-agents already mints ULIDs in
52
+ * its dispatch path), we emit a placeholder shaped like one. The trace
53
+ * is observability-only; downstream consumers tolerate id collisions.
54
+ */
55
+ function placeholderTraceId() {
56
+ // Use randomUUID to derive deterministic-shape entropy then map to the
57
+ // Crockford base32 alphabet. Not a real ULID but matches the regex.
58
+ const hex = randomUUID().replace(/-/g, "");
59
+ const alphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
60
+ let out = "";
61
+ for (let i = 0; i < 26; i++) {
62
+ out += alphabet[Number.parseInt(hex[i % hex.length], 16) % alphabet.length];
63
+ }
64
+ return out;
65
+ }
66
+ function resolveTracePath() {
67
+ const env = process.env.PI_AGENT_TRACE_PATH;
68
+ if (env && env.length > 0)
69
+ return env;
70
+ return path.join(homedir(), ".pi", "traces", "extension-load.jsonl");
71
+ }
72
+ function emitExtensionLoadWarning(extensionName, message, severity) {
73
+ try {
74
+ writeAgentTrace({
75
+ type: "extension_load_warning",
76
+ id: placeholderTraceId(),
77
+ parentId: null,
78
+ timestamp: new Date().toISOString(),
79
+ extension_name: extensionName,
80
+ message,
81
+ severity,
82
+ }, { tracePath: resolveTracePath() });
83
+ }
84
+ catch {
85
+ // Trace emission is observability-only; never fail extension load.
86
+ }
87
+ }
88
+ function buildForbiddenUnion(config) {
89
+ return new Set([...FORBIDDEN_WHOLESALE_OPERATIONS, ...(config?.tool_operations_forbidden ?? [])]);
90
+ }
91
+ export function loadComposites(cwd, pi) {
92
+ const ctx = loadContext(cwd);
93
+ const config = ctx.config;
94
+ const forbidden = buildForbiddenUnion(config);
95
+ const registered = [];
96
+ const skipped = [];
97
+ // Config-absent degrade path (FGAP-121 layer-a): observe via the canonical
98
+ // TraceEntry pipeline (DEC-0002 / TASK-086 precedent). pi.ui.notify is
99
+ // surfaced at the index.ts factory caller IF available there; here we use
100
+ // the trace pipeline so observability is unconditional + queryable.
101
+ if (config === null) {
102
+ emitExtensionLoadWarning("pi-agent-dispatch", `substrate config absent at ${cwd} — composite-loader registered zero composites; the 6 static tools remain available.`, "warning");
103
+ return { registered, skipped, config_absent: true };
104
+ }
105
+ const ops = config?.tool_operations ?? [];
106
+ for (const entry of ops) {
107
+ if (!entry.kind)
108
+ continue; // FEAT-005 static-tool reference, not composite
109
+ if (forbidden.has(entry.canonical_id)) {
110
+ throw new Error(`composite-loader: refusing to register forbidden token: ${entry.canonical_id} (framework L1 + config L5 forbidden union; feedback_no_parallel_ungated_paths).`);
111
+ }
112
+ const binding = KIND_REGISTRY[entry.kind];
113
+ if (!binding) {
114
+ skipped.push({ canonical_id: entry.canonical_id, reason: `unknown kind: ${entry.kind}` });
115
+ continue;
116
+ }
117
+ const instance = entry.instance_params ?? {};
118
+ const tool = {
119
+ name: entry.canonical_id,
120
+ label: entry.display_name ?? entry.canonical_id,
121
+ description: `Composite tool (kind=${entry.kind}) — instance-scoped per config.tool_operations[].`,
122
+ promptSnippet: `Invoke composite ${entry.canonical_id} (kind ${entry.kind}).`,
123
+ parameters: binding.argsSchema,
124
+ async execute(_toolCallId, params) {
125
+ const result = binding.run(cwd, instance, params);
126
+ return {
127
+ details: result,
128
+ content: [{ type: "text", text: JSON.stringify(result) }],
129
+ };
130
+ },
131
+ };
132
+ pi.registerTool(tool);
133
+ registered.push(entry.canonical_id);
134
+ }
135
+ return { registered, skipped, config_absent: false };
136
+ }
137
+ //# sourceMappingURL=composite-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-loader.js","sourceRoot":"","sources":["../src/composite-loader.ts"],"names":[],"mappings":"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"}