@gajae-code/coding-agent 0.16.4 → 0.16.6

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 (198) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/types/capability/types.d.ts +4 -0
  3. package/dist/types/cli/customize-doctor.d.ts +22 -0
  4. package/dist/types/config/file-lock.d.ts +31 -3
  5. package/dist/types/coordinator-mcp/question-state.d.ts +7 -0
  6. package/dist/types/coordinator-mcp/server.d.ts +7 -0
  7. package/dist/types/discovery/helpers.d.ts +33 -0
  8. package/dist/types/exec/bash-executor.d.ts +17 -1
  9. package/dist/types/exec/bash-shell-guardian.d.ts +15 -0
  10. package/dist/types/exec/bash-shell-runtime-entry.d.ts +2 -0
  11. package/dist/types/exec/bash-shell-signals.d.ts +2 -0
  12. package/dist/types/exec/bash-shell-supervisor-entry.d.ts +1 -0
  13. package/dist/types/exec/bash-shell-supervisor.d.ts +10 -0
  14. package/dist/types/exec/bash-shell-worker-entry.d.ts +2 -0
  15. package/dist/types/exec/bash-shell-worker-protocol.d.ts +63 -0
  16. package/dist/types/exec/bash-shell-worker.d.ts +1 -0
  17. package/dist/types/exec/isolated-shell.d.ts +19 -0
  18. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +9 -0
  19. package/dist/types/extensibility/extensions/function-hooks.d.ts +169 -0
  20. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  21. package/dist/types/extensibility/extensions/runner.d.ts +46 -6
  22. package/dist/types/extensibility/extensions/types.d.ts +16 -0
  23. package/dist/types/extensibility/extensions/wrapper.d.ts +3 -1
  24. package/dist/types/extensibility/gjc-plugins/constrained-hooks.d.ts +35 -2
  25. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  26. package/dist/types/extensibility/hooks/types.d.ts +4 -0
  27. package/dist/types/extensibility/runtime-skill-discovery.d.ts +4 -1
  28. package/dist/types/extensibility/skill-management.d.ts +9 -4
  29. package/dist/types/extensibility/skills.d.ts +2 -0
  30. package/dist/types/gjc-runtime/empty-delete-gc.d.ts +4 -1
  31. package/dist/types/gjc-runtime/linux-proc.d.ts +2 -0
  32. package/dist/types/gjc-runtime/session-state-lock.d.ts +12 -0
  33. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +41 -3
  34. package/dist/types/gjc-runtime/ultragoal-succession.d.ts +251 -0
  35. package/dist/types/modes/components/bash-execution.d.ts +1 -0
  36. package/dist/types/modes/components/execution-shared.d.ts +1 -0
  37. package/dist/types/modes/interactive-mode.d.ts +2 -0
  38. package/dist/types/modes/types.d.ts +6 -0
  39. package/dist/types/sdk/broker/ensure.d.ts +32 -0
  40. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +0 -1
  41. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  42. package/dist/types/sdk/broker/startup-failure.d.ts +4 -1
  43. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -0
  44. package/dist/types/sdk/bus/chat-effect-journal.d.ts +17 -2
  45. package/dist/types/sdk/bus/conversation-store.d.ts +11 -0
  46. package/dist/types/sdk/bus/lifecycle-commands.d.ts +8 -8
  47. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  48. package/dist/types/sdk/bus/topic-registry.d.ts +8 -0
  49. package/dist/types/sdk/client/client.d.ts +6 -0
  50. package/dist/types/sdk/host/host.d.ts +3 -1
  51. package/dist/types/sdk/host/session-runtime.d.ts +4 -2
  52. package/dist/types/sdk/lifecycle/service.d.ts +1 -0
  53. package/dist/types/sdk/router/session-router.d.ts +10 -2
  54. package/dist/types/sdk/session.d.ts +5 -1
  55. package/dist/types/session/agent-session.d.ts +2 -0
  56. package/dist/types/session/fallback-chain-controller.d.ts +15 -0
  57. package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
  58. package/dist/types/session/messages.d.ts +1 -0
  59. package/dist/types/session/session-manager.d.ts +3 -1
  60. package/dist/types/setup/paseo/announce.d.ts +4 -3
  61. package/dist/types/skill-state/canonical-skills.d.ts +4 -0
  62. package/dist/types/system-prompt.d.ts +8 -0
  63. package/dist/types/task/executor.d.ts +3 -0
  64. package/dist/types/tools/bash.d.ts +1 -0
  65. package/dist/types/tools/github-cache.d.ts +2 -1
  66. package/dist/types/tools/index.d.ts +2 -0
  67. package/package.json +9 -7
  68. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  69. package/src/capability/index.ts +14 -2
  70. package/src/capability/types.ts +4 -0
  71. package/src/cli/customize-doctor.ts +155 -12
  72. package/src/cli.ts +22 -0
  73. package/src/commands/sdk.ts +74 -31
  74. package/src/config/file-lock-gc.ts +40 -2
  75. package/src/config/file-lock.ts +198 -14
  76. package/src/config/model-preset-registry.ts +115 -16
  77. package/src/config/model-registry.ts +16 -12
  78. package/src/coordinator-mcp/question-state.ts +33 -2
  79. package/src/coordinator-mcp/server.ts +477 -101
  80. package/src/crash/sanitize.ts +1 -1
  81. package/src/defaults/gjc/skills/ultragoal/SKILL.md +22 -0
  82. package/src/discovery/builtin.ts +50 -62
  83. package/src/discovery/helpers.ts +297 -32
  84. package/src/exec/bash-executor.ts +330 -50
  85. package/src/exec/bash-shell-guardian.ts +388 -0
  86. package/src/exec/bash-shell-runtime-entry.ts +5 -0
  87. package/src/exec/bash-shell-signals.ts +29 -0
  88. package/src/exec/bash-shell-supervisor-entry.ts +3 -0
  89. package/src/exec/bash-shell-supervisor.ts +359 -0
  90. package/src/exec/bash-shell-worker-entry.ts +5 -0
  91. package/src/exec/bash-shell-worker-protocol.ts +36 -0
  92. package/src/exec/bash-shell-worker.ts +116 -0
  93. package/src/exec/isolated-shell.ts +393 -0
  94. package/src/extensibility/extensions/function-hooks-internal.ts +73 -0
  95. package/src/extensibility/extensions/function-hooks.ts +959 -0
  96. package/src/extensibility/extensions/index.ts +22 -0
  97. package/src/extensibility/extensions/loader.ts +53 -2
  98. package/src/extensibility/extensions/runner.ts +895 -264
  99. package/src/extensibility/extensions/types.ts +42 -0
  100. package/src/extensibility/extensions/wrapper.ts +56 -15
  101. package/src/extensibility/gjc-plugins/compiler.ts +20 -2
  102. package/src/extensibility/gjc-plugins/constrained-hooks.ts +321 -23
  103. package/src/extensibility/gjc-plugins/registry.ts +113 -1
  104. package/src/extensibility/gjc-plugins/runtime-quarantine.ts +20 -2
  105. package/src/extensibility/gjc-plugins/schema.ts +121 -4
  106. package/src/extensibility/gjc-plugins/types.ts +25 -0
  107. package/src/extensibility/hooks/loader.ts +133 -16
  108. package/src/extensibility/hooks/types.ts +28 -0
  109. package/src/extensibility/runtime-skill-discovery.ts +80 -47
  110. package/src/extensibility/skill-management.ts +200 -52
  111. package/src/extensibility/skills.ts +29 -9
  112. package/src/gjc-runtime/empty-delete-gc.ts +94 -4
  113. package/src/gjc-runtime/gc-runtime.ts +29 -0
  114. package/src/gjc-runtime/linux-proc.ts +14 -0
  115. package/src/gjc-runtime/session-state-lock.ts +51 -8
  116. package/src/gjc-runtime/session-state-sidecar.ts +27 -11
  117. package/src/gjc-runtime/state-writer.ts +30 -29
  118. package/src/gjc-runtime/ultragoal-runtime.ts +189 -47
  119. package/src/gjc-runtime/ultragoal-succession.ts +1667 -0
  120. package/src/gjc-runtime/workflow-manifest.generated.json +52 -1
  121. package/src/gjc-runtime/workflow-manifest.ts +16 -0
  122. package/src/harness-control-plane/session-lease.ts +2 -2
  123. package/src/internal-urls/docs-index.generated.ts +6 -6
  124. package/src/lsp/config.ts +10 -3
  125. package/src/lsp/index.ts +1 -1
  126. package/src/lsp/path-trust.ts +69 -14
  127. package/src/main.ts +6 -1
  128. package/src/modes/acp/acp-agent.ts +16 -3
  129. package/src/modes/components/bash-execution.ts +4 -1
  130. package/src/modes/components/execution-shared.ts +4 -1
  131. package/src/modes/components/session-observer-overlay.ts +1 -1
  132. package/src/modes/components/tool-status-header.ts +3 -1
  133. package/src/modes/controllers/command-controller.ts +1 -0
  134. package/src/modes/controllers/event-controller.ts +3 -2
  135. package/src/modes/controllers/extension-ui-controller.ts +1 -0
  136. package/src/modes/controllers/input-controller.ts +6 -3
  137. package/src/modes/interactive-mode.ts +6 -5
  138. package/src/modes/print-mode.ts +5 -1
  139. package/src/modes/prompt-action-autocomplete.ts +54 -27
  140. package/src/modes/runtime-init.ts +2 -0
  141. package/src/modes/types.ts +16 -3
  142. package/src/modes/utils/ui-helpers.ts +1 -0
  143. package/src/prompts/tools/subagent.md +1 -0
  144. package/src/runtime-mcp/manager.ts +20 -2
  145. package/src/runtime-mcp/pool.test.ts +20 -11
  146. package/src/sdk/broker/broker.ts +119 -28
  147. package/src/sdk/broker/ensure.ts +248 -41
  148. package/src/sdk/broker/lifecycle-ledger.ts +7 -17
  149. package/src/sdk/broker/lifecycle.ts +56 -6
  150. package/src/sdk/broker/startup-failure.ts +15 -6
  151. package/src/sdk/bus/chat-daemon-control.ts +3 -1
  152. package/src/sdk/bus/chat-daemon-runtime.ts +35 -5
  153. package/src/sdk/bus/chat-effect-journal.ts +132 -7
  154. package/src/sdk/bus/conversation-store.ts +29 -0
  155. package/src/sdk/bus/lifecycle-commands.ts +17 -11
  156. package/src/sdk/bus/slack-conversation.ts +1 -0
  157. package/src/sdk/bus/slack-daemon.ts +387 -48
  158. package/src/sdk/bus/telegram-daemon.ts +16 -2
  159. package/src/sdk/bus/topic-registry.ts +19 -0
  160. package/src/sdk/cli/master-cli.ts +1 -1
  161. package/src/sdk/cli/session-cli.ts +2 -2
  162. package/src/sdk/client/client.ts +66 -17
  163. package/src/sdk/host/host.ts +29 -9
  164. package/src/sdk/host/query/revision-store.ts +19 -13
  165. package/src/sdk/host/reverse-leases.ts +7 -4
  166. package/src/sdk/host/session-runtime.test.ts +50 -8
  167. package/src/sdk/host/session-runtime.ts +158 -51
  168. package/src/sdk/lifecycle/service.ts +4 -0
  169. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  170. package/src/sdk/router/session-router.ts +89 -38
  171. package/src/sdk/session.ts +155 -26
  172. package/src/sdk/transport/relay.ts +35 -13
  173. package/src/session/agent-session.ts +117 -52
  174. package/src/session/fallback-chain-controller.ts +38 -0
  175. package/src/session/internal/managed-session-storage.ts +19 -5
  176. package/src/session/messages.ts +5 -0
  177. package/src/session/session-manager.ts +29 -5
  178. package/src/session-import/redact.ts +5 -1
  179. package/src/setup/paseo/announce.ts +49 -43
  180. package/src/setup/paseo/setup-deps.ts +1 -1
  181. package/src/skill-state/canonical-skills.ts +12 -0
  182. package/src/skills/skills.test.ts +41 -21
  183. package/src/system-prompt.ts +25 -3
  184. package/src/task/executor.ts +5 -0
  185. package/src/task/index.ts +4 -0
  186. package/src/tools/bash.ts +28 -11
  187. package/src/tools/gh.ts +26 -3
  188. package/src/tools/github-cache.ts +68 -16
  189. package/src/tools/index.ts +2 -0
  190. package/src/tools/job.ts +13 -2
  191. package/src/tools/read.ts +14 -2
  192. package/src/tools/render-utils.ts +1 -3
  193. package/src/tools/resource-gc.ts +28 -8
  194. package/src/tools/skill-discovery.ts +5 -0
  195. package/src/tools/skill.ts +15 -0
  196. package/src/tools/subagent.ts +3 -2
  197. package/src/tools/tool-catalog.generated.ts +2 -2
  198. package/src/tools/ultragoal-ask-guard.ts +2 -2
@@ -49,6 +49,7 @@ import type { ExecOptions, ExecResult } from "../../exec/exec";
49
49
  import type { CustomEditor } from "../../modes/components/custom-editor";
50
50
  import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/workflow-gate-broker";
51
51
  import type { Theme } from "../../modes/theme/theme";
52
+ import type { AgentSessionEventListener } from "../../session/agent-session";
52
53
  import type {
53
54
  ClientBridge,
54
55
  ClientBridgePermissionOption,
@@ -103,6 +104,12 @@ import type {
103
104
  TurnStartEvent,
104
105
  } from "../shared-events";
105
106
  import type { SlashCommandInfo } from "../slash-commands";
107
+ import type {
108
+ FunctionHook,
109
+ FunctionHookEventType,
110
+ FunctionHookPayloadFor,
111
+ FunctionHookRegistrationOptions,
112
+ } from "./function-hooks";
106
113
 
107
114
  export type { AppKeybinding, KeybindingsManager } from "../../config/keybindings";
108
115
  export type { ExecOptions, ExecResult } from "../../exec/exec";
@@ -530,6 +537,8 @@ export interface ExtensionContext {
530
537
  getJobs(): unknown;
531
538
  /** Subscribe to foreground-wait folds (`bash_folded`). Returns an unsubscribe function. */
532
539
  onJobFold?(listener: (event: JobFoldEvent) => void): () => void;
540
+ /** Observe session events synchronously in producer order. Returns an unsubscribe function. */
541
+ onSessionEvent?(listener: AgentSessionEventListener): () => void;
533
542
  /** Typed skill and mode controls exposed to the SDK host. */
534
543
  invokeSkill?(
535
544
  name: string,
@@ -750,6 +759,8 @@ export type { AgentEndEvent, TurnEndEvent, TurnStartEvent } from "../shared-even
750
759
  /** Fired when an agent loop starts. `sdkRunToken` is an internal SDK queue-owner binding. */
751
760
  export interface AgentStartEvent extends SharedAgentStartEvent {
752
761
  sdkRunToken?: string;
762
+ /** Complete set of SDK queue owners consumed together by this run. */
763
+ sdkRunTokens?: string[];
753
764
  }
754
765
 
755
766
  /** Fired when an agent run fails before emitting agent_end. The error is the
@@ -1234,6 +1245,18 @@ export interface ExtensionAPI {
1234
1245
  on(event: "user_bash", handler: ExtensionHandler<UserBashEvent, UserBashEventResult>): void;
1235
1246
  on(event: "user_python", handler: ExtensionHandler<UserPythonEvent, UserPythonEventResult>): void;
1236
1247
 
1248
+ /**
1249
+ * Register capability-scoped middleware on the authoritative ExtensionRunner.
1250
+ * The host supplies grants; an extension cannot self-grant capabilities.
1251
+ * Wildcard registrations receive a redacted payload unless the host explicitly
1252
+ * grants a tool inspection/transform capability.
1253
+ */
1254
+ registerFunctionHook<T extends FunctionHookEventType>(
1255
+ event: T,
1256
+ handler: FunctionHook<FunctionHookPayloadFor<T>>,
1257
+ options?: FunctionHookRegistrationOptions,
1258
+ ): void;
1259
+
1237
1260
  // =========================================================================
1238
1261
  // Tool Registration
1239
1262
  // =========================================================================
@@ -1692,6 +1715,8 @@ export interface ExtensionContextActions {
1692
1715
  getJobs?: () => unknown;
1693
1716
  /** Subscribe to foreground-wait folds so the SDK host can publish `bash_folded`. Returns an unsubscribe. */
1694
1717
  onJobFold?: (listener: (event: JobFoldEvent) => void) => () => void;
1718
+ /** Ordered, synchronous AgentSession subscription, independent of async extension hooks. */
1719
+ onSessionEvent?: (listener: AgentSessionEventListener) => () => void;
1695
1720
  setSdkPermissionProvider?: (
1696
1721
  provider:
1697
1722
  | ((
@@ -1752,6 +1777,23 @@ export interface Extension {
1752
1777
  shortcuts: Map<KeyId, ExtensionShortcut>;
1753
1778
  }
1754
1779
 
1780
+ export type {
1781
+ FunctionHook,
1782
+ FunctionHookAuditRecord,
1783
+ FunctionHookCapabilities,
1784
+ FunctionHookCapability,
1785
+ FunctionHookEventType,
1786
+ FunctionHookGrant,
1787
+ FunctionHookGrantInput,
1788
+ FunctionHookNext,
1789
+ FunctionHookOperation,
1790
+ FunctionHookPayloadFor,
1791
+ FunctionHookProvenance,
1792
+ FunctionHookRegistration,
1793
+ FunctionHookRegistrationOptions,
1794
+ FunctionHookResult,
1795
+ } from "./function-hooks";
1796
+
1755
1797
  /** Result of loading extensions. */
1756
1798
  export interface LoadExtensionsResult {
1757
1799
  extensions: Extension[];
@@ -2,12 +2,24 @@
2
2
  * Tool wrappers for extensions.
3
3
  */
4
4
  import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@gajae-code/agent-core";
5
- import type { ImageContent, Static, TextContent, TSchema } from "@gajae-code/ai/core";
5
+ import {
6
+ type ImageContent,
7
+ type Static,
8
+ type TextContent,
9
+ type ToolCall,
10
+ type TSchema,
11
+ validateToolArguments,
12
+ } from "@gajae-code/ai/core";
6
13
  import type { Theme } from "../../modes/theme/theme";
14
+ import { ToolAbortError } from "../../tools/tool-errors";
7
15
  import { applyToolProxy } from "../tool-proxy";
8
16
  import type { ExtensionRunner } from "./runner";
9
17
  import type { RegisteredTool, ToolCallEventResult } from "./types";
10
18
 
19
+ function toolAbortReason(signal: AbortSignal | undefined, fallback: string): Error {
20
+ return signal?.reason instanceof Error ? signal.reason : new ToolAbortError(fallback);
21
+ }
22
+
11
23
  /**
12
24
  * Adapts a RegisteredTool into an AgentTool.
13
25
  */
@@ -92,6 +104,11 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
92
104
  applyToolProxy(tool, this);
93
105
  }
94
106
 
107
+ /** Host composition hook: guards must run inside Function Hook preflight. */
108
+ getInnerTool(): AgentTool<TParameters, TDetails> {
109
+ return this.tool;
110
+ }
111
+
95
112
  /**
96
113
  * Forward browser mode changes when available.
97
114
  */
@@ -109,19 +126,21 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
109
126
  context?: AgentToolContext,
110
127
  ) {
111
128
  const scope = context?.attemptScope;
129
+ const toolCallEvent = {
130
+ type: "tool_call" as const,
131
+ toolName: this.tool.name,
132
+ toolCallId,
133
+ input: params as Record<string, unknown>,
134
+ };
112
135
  // Emit tool_call event - extensions can block execution
113
136
  if (this.runner.hasHandlers("tool_call")) {
114
137
  try {
115
- const callResult = (await this.runner.emitToolCall(
116
- {
117
- type: "tool_call",
118
- toolName: this.tool.name,
119
- toolCallId,
120
- input: params as Record<string, unknown>,
121
- },
122
- scope,
123
- )) as ToolCallEventResult | undefined;
138
+ const callResult = (await this.runner.emitToolCall(toolCallEvent, scope, {
139
+ signal,
140
+ correlationId: toolCallId,
141
+ })) as ToolCallEventResult | undefined;
124
142
 
143
+ if (signal?.aborted) throw toolAbortReason(signal, "Tool call aborted during Function Hook mediation");
125
144
  if (callResult?.block) {
126
145
  const reason = callResult.reason || "Tool execution was blocked by an extension";
127
146
  throw new Error(reason);
@@ -133,14 +152,28 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
133
152
  throw new Error(`Extension failed, blocking execution: ${String(err)}`);
134
153
  }
135
154
  }
155
+ if (toolCallEvent.input !== params || !this.tool.lenientArgValidation) {
156
+ params = validateToolArguments(this.tool, {
157
+ type: "toolCall",
158
+ id: toolCallId,
159
+ name: this.tool.name,
160
+ arguments: toolCallEvent.input,
161
+ } satisfies ToolCall) as Static<TParameters>;
162
+ }
136
163
 
137
164
  // Execute the actual tool
138
165
  let result: { content: any; details?: TDetails };
139
166
  let executionError: Error | undefined;
167
+ const mediatesToolResult = this.runner.hasToolResultMediation(this.tool.name);
168
+ const deliversToolResult = this.runner.hasHandlers("tool_result");
169
+ const effectiveOnUpdate = mediatesToolResult ? undefined : onUpdate;
140
170
 
141
171
  try {
142
- result = await this.tool.execute(toolCallId, params, signal, onUpdate, context);
172
+ result = await this.tool.execute(toolCallId, params, signal, effectiveOnUpdate, context);
143
173
  } catch (err) {
174
+ if (err instanceof ToolAbortError || signal?.aborted) {
175
+ throw err instanceof ToolAbortError ? err : toolAbortReason(signal, "Tool execution aborted");
176
+ }
144
177
  executionError = err instanceof Error ? err : new Error(String(err));
145
178
  result = {
146
179
  content: [{ type: "text", text: executionError.message }],
@@ -149,7 +182,8 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
149
182
  }
150
183
 
151
184
  // Emit tool_result event - extensions can modify the result and error status
152
- if (this.runner.hasHandlers("tool_result")) {
185
+ if (signal?.aborted) throw toolAbortReason(signal, "Tool execution aborted");
186
+ if (deliversToolResult) {
153
187
  const resultResult = await this.runner.emitToolResult(
154
188
  {
155
189
  type: "tool_result",
@@ -161,11 +195,17 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
161
195
  isError: !!executionError,
162
196
  },
163
197
  scope,
198
+ { signal, correlationId: toolCallId },
164
199
  );
200
+ if (signal?.aborted) throw toolAbortReason(signal, "Tool result mediation aborted");
165
201
 
166
202
  if (resultResult) {
167
- const modifiedContent: (TextContent | ImageContent)[] = resultResult.content ?? result.content;
168
- const modifiedDetails = (resultResult.details ?? result.details) as TDetails;
203
+ const modifiedContent: (TextContent | ImageContent)[] = Object.hasOwn(resultResult, "content")
204
+ ? (resultResult.content ?? [])
205
+ : result.content;
206
+ const modifiedDetails = (
207
+ Object.hasOwn(resultResult, "details") ? resultResult.details : result.details
208
+ ) as TDetails;
169
209
 
170
210
  // Extension can override error status
171
211
  if (resultResult.isError === true && !executionError) {
@@ -181,7 +221,8 @@ export class ExtensionToolWrapper<TParameters extends TSchema = TSchema, TDetail
181
221
 
182
222
  // Error status unchanged, but content/details may be modified
183
223
  if (executionError) {
184
- throw executionError;
224
+ const textBlocks = modifiedContent.filter((content): content is TextContent => content.type === "text");
225
+ throw new Error(textBlocks.map(content => content.text).join("\n") || "Tool execution failed");
185
226
  }
186
227
  return { content: modifiedContent, details: modifiedDetails };
187
228
  }
@@ -3,6 +3,7 @@ import * as nodeFs from "node:fs";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path from "node:path";
5
5
  import { parseFrontmatter, pathIsWithin } from "@gajae-code/utils";
6
+ import { functionHookGrantHash, normalizeFunctionHookGrant } from "../extensions/function-hooks";
6
7
  import { classifyStdioInvocation } from "./mcp-policy";
7
8
  import { canonicalizeJsonSchema, extractDeclaredToolSchema, schemaHash } from "./metadata";
8
9
  import { resolveWithinRoot } from "./paths";
@@ -447,7 +448,16 @@ export async function compileGjcPluginBundle(root: string): Promise<NormalizedGj
447
448
  `GJC plugin hook "${hook.name}": tool_result requires the after phase`,
448
449
  );
449
450
  }
450
- hooks.push({
451
+ const grant = normalizeFunctionHookGrant({
452
+ capabilities: hook.capabilities,
453
+ networkDestinations: hook.networkDestinations,
454
+ filesystemRoots: hook.filesystemRoots,
455
+ });
456
+ const functionHook =
457
+ hook.capabilities !== undefined ||
458
+ hook.networkDestinations !== undefined ||
459
+ hook.filesystemRoots !== undefined;
460
+ const normalizedHook: NormalizedHookSurface = {
451
461
  extensionId: surfaceIds.hook(hook.event, hook.phase, hook.target, hook.name),
452
462
  name: hook.name,
453
463
  event: hook.event,
@@ -456,7 +466,15 @@ export async function compileGjcPluginBundle(root: string): Promise<NormalizedGj
456
466
  relativePath: hook.path,
457
467
  sha256: digest,
458
468
  implementationHash: digest,
459
- });
469
+ };
470
+ if (functionHook) {
471
+ normalizedHook.capabilities = [...grant.capabilities];
472
+ normalizedHook.networkDestinations = [...grant.networkDestinations];
473
+ normalizedHook.filesystemRoots = [...grant.filesystemRoots];
474
+ normalizedHook.capabilityHash = functionHookGrantHash(grant);
475
+ normalizedHook.functionHook = true;
476
+ }
477
+ hooks.push(normalizedHook);
460
478
  }
461
479
 
462
480
  const mcps: NormalizedMcpSurface[] = [];