@gajae-code/coding-agent 0.12.6 → 0.12.8

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 (150) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/types/async/job-manager.d.ts +36 -1
  3. package/dist/types/commands/notify.d.ts +4 -0
  4. package/dist/types/config/file-lock.d.ts +3 -0
  5. package/dist/types/config/model-discovery-manager.d.ts +6 -1
  6. package/dist/types/config/model-registry.d.ts +2 -0
  7. package/dist/types/config/resolve-config-value.d.ts +2 -1
  8. package/dist/types/config/settings-schema.d.ts +17 -1
  9. package/dist/types/eval/backend.d.ts +2 -2
  10. package/dist/types/exec/bash-executor.d.ts +8 -10
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
  12. package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
  13. package/dist/types/main.d.ts +1 -0
  14. package/dist/types/modes/components/hook-selector.d.ts +20 -0
  15. package/dist/types/modes/components/notifications-settings-editor.d.ts +53 -4
  16. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -1
  17. package/dist/types/modes/controllers/selector-controller.d.ts +4 -2
  18. package/dist/types/sdk/broker/broker.d.ts +5 -1
  19. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +1 -0
  20. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -3
  21. package/dist/types/sdk/bus/config.d.ts +75 -113
  22. package/dist/types/sdk/bus/discord-live-provider.d.ts +8 -2
  23. package/dist/types/sdk/bus/discord-provider.d.ts +19 -0
  24. package/dist/types/sdk/bus/index.d.ts +1 -1
  25. package/dist/types/sdk/bus/notification-orchestration.d.ts +127 -18
  26. package/dist/types/sdk/bus/notification-service.d.ts +31 -4
  27. package/dist/types/sdk/bus/session-control.d.ts +8 -3
  28. package/dist/types/sdk/bus/slack-live-provider.d.ts +9 -2
  29. package/dist/types/sdk/bus/slack-provider.d.ts +22 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  31. package/dist/types/sdk/client/discovery.d.ts +2 -1
  32. package/dist/types/sdk/host/host.d.ts +2 -23
  33. package/dist/types/sdk/host/query/handlers.d.ts +2 -0
  34. package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
  35. package/dist/types/sdk/index.d.ts +1 -0
  36. package/dist/types/sdk/providers.d.ts +22 -0
  37. package/dist/types/sdk/session-directory.d.ts +1 -1
  38. package/dist/types/session/artifacts.d.ts +5 -8
  39. package/dist/types/session/client-bridge.d.ts +0 -14
  40. package/dist/types/session/internal/managed-session-scope.d.ts +17 -1
  41. package/dist/types/session/internal/managed-session-storage.d.ts +11 -4
  42. package/dist/types/session/internal/native-publish-outcome.d.ts +1 -0
  43. package/dist/types/session/internal/session-open-errors.d.ts +5 -0
  44. package/dist/types/session/messages.d.ts +1 -4
  45. package/dist/types/session/session-manager.d.ts +7 -6
  46. package/dist/types/session/session-storage.d.ts +2 -0
  47. package/dist/types/session/streaming-output.d.ts +0 -32
  48. package/dist/types/task/receipt.d.ts +1 -2
  49. package/dist/types/task/types.d.ts +43 -178
  50. package/dist/types/tools/bash.d.ts +1 -1
  51. package/dist/types/tools/fetch.d.ts +0 -3
  52. package/dist/types/tools/output-meta.d.ts +5 -37
  53. package/dist/types/tools/subagent.d.ts +15 -0
  54. package/dist/types/tools/todo-write.d.ts +3 -0
  55. package/dist/types/tools/tool-result.d.ts +0 -1
  56. package/package.json +10 -8
  57. package/scripts/build-sdk-package-smoke.ts +51 -0
  58. package/scripts/capture-notifications-settings-showcase.ts +1 -1
  59. package/src/async/job-manager.ts +430 -46
  60. package/src/cli/notify-cli.ts +204 -94
  61. package/src/commands/notify.ts +19 -8
  62. package/src/config/file-lock.ts +6 -0
  63. package/src/config/model-discovery-manager.ts +50 -8
  64. package/src/config/model-registry.ts +862 -75
  65. package/src/config/resolve-config-value.ts +10 -8
  66. package/src/config/settings-schema.ts +25 -15
  67. package/src/config/settings.ts +15 -15
  68. package/src/cursor.ts +13 -20
  69. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
  70. package/src/eval/backend.ts +2 -2
  71. package/src/exec/bash-executor.ts +151 -425
  72. package/src/gjc-runtime/session-state-sidecar.ts +12 -2
  73. package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
  74. package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
  75. package/src/gjc-runtime/workflow-manifest.ts +0 -12
  76. package/src/internal-urls/agent-protocol.ts +38 -98
  77. package/src/internal-urls/artifact-protocol.ts +17 -28
  78. package/src/internal-urls/docs-index.generated.ts +7 -7
  79. package/src/internal-urls/registry-helpers.ts +0 -62
  80. package/src/main.ts +43 -7
  81. package/src/modes/DESIGN.md +11 -11
  82. package/src/modes/acp/acp-agent.ts +0 -24
  83. package/src/modes/acp/acp-client-bridge.ts +11 -154
  84. package/src/modes/components/hook-selector.ts +155 -44
  85. package/src/modes/components/model-selector.ts +242 -49
  86. package/src/modes/components/notifications-settings-editor.ts +631 -85
  87. package/src/modes/controllers/extension-ui-controller.ts +114 -15
  88. package/src/modes/controllers/selector-controller.ts +464 -50
  89. package/src/prompts/tools/bash.md +2 -2
  90. package/src/sdk/acp/adapter.ts +9 -200
  91. package/src/sdk/broker/broker.ts +54 -10
  92. package/src/sdk/broker/lifecycle-ledger.ts +34 -1
  93. package/src/sdk/broker/lifecycle.ts +47 -50
  94. package/src/sdk/bus/chat-daemon-cli.ts +12 -5
  95. package/src/sdk/bus/chat-daemon-control.ts +35 -8
  96. package/src/sdk/bus/chat-daemon-runtime.ts +16 -2
  97. package/src/sdk/bus/config.ts +534 -286
  98. package/src/sdk/bus/discord-live-provider.ts +57 -2
  99. package/src/sdk/bus/discord-provider.ts +21 -0
  100. package/src/sdk/bus/index.ts +105 -359
  101. package/src/sdk/bus/notification-orchestration.ts +371 -68
  102. package/src/sdk/bus/notification-service.ts +431 -130
  103. package/src/sdk/bus/session-control.ts +119 -44
  104. package/src/sdk/bus/slack-live-provider.ts +78 -2
  105. package/src/sdk/bus/slack-provider.ts +24 -0
  106. package/src/sdk/bus/telegram-daemon-cli.ts +9 -2
  107. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  108. package/src/sdk/bus/telegram-daemon-control.ts +3 -3
  109. package/src/sdk/bus/telegram-daemon.ts +23 -15
  110. package/src/sdk/client/discovery.ts +10 -4
  111. package/src/sdk/host/host.ts +9 -187
  112. package/src/sdk/host/query/handlers.ts +20 -1
  113. package/src/sdk/host/reverse-leases.ts +42 -249
  114. package/src/sdk/index.ts +1 -0
  115. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  116. package/src/sdk/protocol/operation-registry.ts +5 -2
  117. package/src/sdk/providers.ts +51 -0
  118. package/src/sdk/session-directory.ts +1 -0
  119. package/src/sdk/session.ts +10 -7
  120. package/src/session/agent-session.ts +59 -202
  121. package/src/session/artifacts.ts +74 -211
  122. package/src/session/client-bridge.ts +0 -15
  123. package/src/session/internal/managed-session-scope.ts +981 -450
  124. package/src/session/internal/managed-session-storage.ts +362 -124
  125. package/src/session/internal/native-publish-outcome.ts +3 -1
  126. package/src/session/internal/session-open-errors.ts +11 -0
  127. package/src/session/messages.ts +7 -32
  128. package/src/session/session-manager.ts +48 -15
  129. package/src/session/session-storage.ts +72 -5
  130. package/src/session/streaming-output.ts +40 -198
  131. package/src/slash-commands/builtin-registry.ts +82 -4
  132. package/src/task/index.ts +237 -65
  133. package/src/task/receipt.ts +2 -4
  134. package/src/task/render.ts +0 -30
  135. package/src/task/types.ts +10 -34
  136. package/src/tools/ask.ts +1 -1
  137. package/src/tools/bash.ts +239 -818
  138. package/src/tools/eval.ts +4 -99
  139. package/src/tools/fetch.ts +15 -77
  140. package/src/tools/find.ts +3 -7
  141. package/src/tools/output-meta.ts +114 -696
  142. package/src/tools/search.ts +4 -9
  143. package/src/tools/subagent.ts +97 -75
  144. package/src/tools/telegram-send.ts +4 -2
  145. package/src/tools/todo-write.ts +47 -0
  146. package/src/tools/tool-result.ts +0 -1
  147. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
  148. package/dist/types/utils/artifact-id.d.ts +0 -2
  149. package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
  150. package/src/utils/artifact-id.ts +0 -6
@@ -16,22 +16,24 @@ const commandInFlight = new Map<string, Promise<string | undefined>>();
16
16
  * Resolve a config value (API key, header value, etc.) to an actual value.
17
17
  * - If starts with "!", executes the rest as a shell command and uses stdout (cached)
18
18
  * - Otherwise checks environment variable first, then treats as literal (not cached)
19
+ * - `cacheScope` isolates command-cache entries when a caller rotates its config
19
20
  */
20
- export async function resolveConfigValue(config: string): Promise<string | undefined> {
21
+ export async function resolveConfigValue(config: string, cacheScope?: string): Promise<string | undefined> {
21
22
  if (config.startsWith("!")) {
22
- return await executeCommand(config);
23
+ return await executeCommand(config, cacheScope);
23
24
  }
24
25
  const envValue = process.env[config];
25
26
  return envValue || config;
26
27
  }
27
28
 
28
- async function executeCommand(commandConfig: string): Promise<string | undefined> {
29
- const cached = commandResultCache.get(commandConfig);
29
+ async function executeCommand(commandConfig: string, cacheScope?: string): Promise<string | undefined> {
30
+ const cacheKey = cacheScope === undefined ? commandConfig : `${cacheScope}\u0000${commandConfig}`;
31
+ const cached = commandResultCache.get(cacheKey);
30
32
  if (cached !== undefined) {
31
33
  return cached;
32
34
  }
33
35
 
34
- const existing = commandInFlight.get(commandConfig);
36
+ const existing = commandInFlight.get(cacheKey);
35
37
  if (existing) {
36
38
  return await existing;
37
39
  }
@@ -40,15 +42,15 @@ async function executeCommand(commandConfig: string): Promise<string | undefined
40
42
  const promise = runShellCommand(command, 10_000)
41
43
  .then(result => {
42
44
  if (result !== undefined) {
43
- commandResultCache.set(commandConfig, result);
45
+ commandResultCache.set(cacheKey, result);
44
46
  }
45
47
  return result;
46
48
  })
47
49
  .finally(() => {
48
- commandInFlight.delete(commandConfig);
50
+ commandInFlight.delete(cacheKey);
49
51
  });
50
52
 
51
- commandInFlight.set(commandConfig, promise);
53
+ commandInFlight.set(cacheKey, promise);
52
54
  return await promise;
53
55
  }
54
56
 
@@ -149,7 +149,7 @@ export type AnyUiMetadata = UiBase & {
149
149
 
150
150
  interface BooleanDef {
151
151
  type: "boolean";
152
- default: boolean;
152
+ default?: boolean;
153
153
  ui?: UiBoolean;
154
154
  }
155
155
 
@@ -285,6 +285,7 @@ export const SETTINGS_SCHEMA = {
285
285
 
286
286
  // Notifications (shared daemon with Telegram/Discord/Slack presentation adapters)
287
287
  "notifications.enabled": { type: "boolean", default: false },
288
+ "notifications.telegram.enabled": { type: "boolean" },
288
289
  "notifications.telegram.botToken": {
289
290
  type: "string",
290
291
  default: undefined,
@@ -336,10 +337,12 @@ export const SETTINGS_SCHEMA = {
336
337
  },
337
338
  },
338
339
  "notifications.telegram.topics.nameTemplate": { type: "string", default: undefined },
340
+ "notifications.discord.enabled": { type: "boolean" },
339
341
  "notifications.discord.botToken": { type: "string", default: undefined },
340
342
  "notifications.discord.applicationId": { type: "string", default: undefined },
341
343
  "notifications.discord.guildId": { type: "string", default: undefined },
342
344
  "notifications.discord.parentChannelId": { type: "string", default: undefined },
345
+ "notifications.slack.enabled": { type: "boolean" },
343
346
  "notifications.slack.botToken": { type: "string", default: undefined },
344
347
  "notifications.slack.appToken": { type: "string", default: undefined },
345
348
  "notifications.slack.workspaceId": { type: "string", default: undefined },
@@ -3646,25 +3649,28 @@ type Schema = typeof SETTINGS_SCHEMA;
3646
3649
  export type SettingPath = keyof Schema;
3647
3650
 
3648
3651
  /** Infer the value type for a setting path */
3649
- export type SettingValue<P extends SettingPath> = Schema[P] extends { type: "boolean" }
3652
+ export type SettingValue<P extends SettingPath> = Schema[P] extends { type: "boolean"; default: boolean }
3650
3653
  ? boolean
3651
- : Schema[P] extends { type: "string" }
3652
- ? string | undefined
3653
- : Schema[P] extends { type: "number" }
3654
- ? number
3655
- : Schema[P] extends { type: "enum"; values: infer V }
3656
- ? V extends readonly string[]
3657
- ? V[number]
3658
- : never
3659
- : Schema[P] extends { type: "array"; default: infer D }
3660
- ? D
3661
- : Schema[P] extends { type: "record"; default: infer D }
3654
+ : Schema[P] extends { type: "boolean" }
3655
+ ? boolean | undefined
3656
+ : Schema[P] extends { type: "string" }
3657
+ ? string | undefined
3658
+ : Schema[P] extends { type: "number" }
3659
+ ? number
3660
+ : Schema[P] extends { type: "enum"; values: infer V }
3661
+ ? V extends readonly string[]
3662
+ ? V[number]
3663
+ : never
3664
+ : Schema[P] extends { type: "array"; default: infer D }
3662
3665
  ? D
3663
- : never;
3666
+ : Schema[P] extends { type: "record"; default: infer D }
3667
+ ? D
3668
+ : never;
3664
3669
 
3665
3670
  /** Get the default value for a setting path */
3666
3671
  export function getDefault<P extends SettingPath>(path: P): SettingValue<P> {
3667
- return SETTINGS_SCHEMA[path].default as SettingValue<P>;
3672
+ const definition = SETTINGS_SCHEMA[path];
3673
+ return ("default" in definition ? definition.default : undefined) as SettingValue<P>;
3668
3674
  }
3669
3675
 
3670
3676
  /** Check if a path has UI metadata (should appear in settings panel) */
@@ -3991,6 +3997,7 @@ export interface MemoryGuardSettings {
3991
3997
  export interface NotificationsSettings {
3992
3998
  enabled: boolean;
3993
3999
  telegram: {
4000
+ enabled?: boolean;
3994
4001
  botToken: string | undefined;
3995
4002
  chatId: string | undefined;
3996
4003
  sound: "all" | "important" | "none";
@@ -4014,16 +4021,19 @@ export interface NotificationsSettings {
4014
4021
  };
4015
4022
  };
4016
4023
  discord: {
4024
+ enabled?: boolean;
4017
4025
  botToken: string | undefined;
4018
4026
  applicationId: string | undefined;
4019
4027
  guildId: string | undefined;
4020
4028
  parentChannelId: string | undefined;
4021
4029
  };
4022
4030
  slack: {
4031
+ enabled?: boolean;
4023
4032
  botToken: string | undefined;
4024
4033
  appToken: string | undefined;
4025
4034
  workspaceId: string | undefined;
4026
4035
  channelId: string | undefined;
4036
+ authorizedUserId: string | undefined;
4027
4037
  };
4028
4038
  redact: boolean;
4029
4039
  verbosity: "lean" | "verbose";
@@ -98,7 +98,7 @@ type DurableBatchRevision = {
98
98
  };
99
99
  type NotificationValidationState = {
100
100
  malformedConfigRoot: boolean;
101
- invalidNotificationConfiguration: boolean;
101
+ invalidNotificationGlobal: boolean;
102
102
  generation: number;
103
103
  };
104
104
  type NotificationValidationRestoreGuard = {
@@ -378,7 +378,7 @@ export class Settings implements NotificationSettingsReader {
378
378
  #hasMalformedConfigRoot = false;
379
379
  /** YAML syntax was unrecoverable, so the loaded defaults are read-only until config.yml is repaired. */
380
380
  #hasRecoveredConfigSyntax = false;
381
- #hasInvalidNotificationConfiguration = false;
381
+ #hasInvalidNotificationGlobal = false;
382
382
  #notificationValidationGeneration = 0;
383
383
  /** Notification subtree fingerprint from the last raw durable config read. */
384
384
  #durableNotificationFingerprint: string | undefined;
@@ -513,7 +513,7 @@ export class Settings implements NotificationSettingsReader {
513
513
  */
514
514
  getNotificationSettingsSnapshot(): NotificationSettingsSnapshot {
515
515
  return parseNotificationSettingsSnapshot(
516
- this.#hasMalformedConfigRoot || this.#hasInvalidNotificationConfiguration ? null : this.#global,
516
+ this.#hasMalformedConfigRoot || this.#hasInvalidNotificationGlobal ? null : this.#rawNotificationConfig,
517
517
  );
518
518
  }
519
519
 
@@ -895,7 +895,7 @@ export class Settings implements NotificationSettingsReader {
895
895
  cloned.#futureSchemaVersion = this.#futureSchemaVersion;
896
896
  cloned.#hasMalformedConfigRoot = this.#hasMalformedConfigRoot;
897
897
  cloned.#hasRecoveredConfigSyntax = this.#hasRecoveredConfigSyntax;
898
- cloned.#hasInvalidNotificationConfiguration = this.#hasInvalidNotificationConfiguration;
898
+ cloned.#hasInvalidNotificationGlobal = this.#hasInvalidNotificationGlobal;
899
899
  cloned.#notificationValidationGeneration = this.#notificationValidationGeneration;
900
900
  cloned.#global = structuredClone(this.#global);
901
901
  cloned.#rawNotificationConfig = structuredClone(this.#rawNotificationConfig);
@@ -1149,7 +1149,7 @@ export class Settings implements NotificationSettingsReader {
1149
1149
  #resetYamlLoadState(): void {
1150
1150
  this.#hasMalformedConfigRoot = false;
1151
1151
  this.#hasRecoveredConfigSyntax = false;
1152
- this.#hasInvalidNotificationConfiguration = false;
1152
+ this.#hasInvalidNotificationGlobal = false;
1153
1153
  this.#schemaReport = { issues: [], valid: true };
1154
1154
  this.#schemaMigrationPending = false;
1155
1155
  this.#futureSchemaVersion = false;
@@ -1211,7 +1211,7 @@ export class Settings implements NotificationSettingsReader {
1211
1211
  parseNotificationSettingsSnapshot(parsedRaw);
1212
1212
  } catch (error) {
1213
1213
  if (!(error instanceof Error) || error.message !== "gjc_notify_daemon_invalid_configuration") throw error;
1214
- this.#hasInvalidNotificationConfiguration = true;
1214
+ this.#hasInvalidNotificationGlobal = true;
1215
1215
  }
1216
1216
  }
1217
1217
  this.#futureSchemaVersion =
@@ -1875,7 +1875,7 @@ export class Settings implements NotificationSettingsReader {
1875
1875
  #notificationValidationState(): NotificationValidationState {
1876
1876
  return {
1877
1877
  malformedConfigRoot: this.#hasMalformedConfigRoot,
1878
- invalidNotificationConfiguration: this.#hasInvalidNotificationConfiguration,
1878
+ invalidNotificationGlobal: this.#hasInvalidNotificationGlobal,
1879
1879
  generation: this.#notificationValidationGeneration,
1880
1880
  };
1881
1881
  }
@@ -1900,7 +1900,7 @@ export class Settings implements NotificationSettingsReader {
1900
1900
  }
1901
1901
  #restoreNotificationValidationState(state: NotificationValidationState): void {
1902
1902
  this.#hasMalformedConfigRoot = state.malformedConfigRoot;
1903
- this.#hasInvalidNotificationConfiguration = state.invalidNotificationConfiguration;
1903
+ this.#hasInvalidNotificationGlobal = state.invalidNotificationGlobal;
1904
1904
  }
1905
1905
  #rejectAtomicNotificationRepairForMalformedRoot(patches: readonly AtomicYamlPatch[], root: unknown): void {
1906
1906
  if (
@@ -1951,17 +1951,17 @@ export class Settings implements NotificationSettingsReader {
1951
1951
  #recomputeNotificationValidationFromRaw(): void {
1952
1952
  if (this.#rawNotificationConfig === undefined) {
1953
1953
  this.#hasMalformedConfigRoot = true;
1954
- this.#hasInvalidNotificationConfiguration = false;
1954
+ this.#hasInvalidNotificationGlobal = false;
1955
1955
  return;
1956
1956
  }
1957
1957
  try {
1958
1958
  parseNotificationSettingsSnapshot(this.#rawNotificationConfig);
1959
1959
  this.#hasMalformedConfigRoot = false;
1960
- this.#hasInvalidNotificationConfiguration = false;
1960
+ this.#hasInvalidNotificationGlobal = false;
1961
1961
  } catch (error) {
1962
1962
  if (error instanceof Error && error.message === "gjc_notify_daemon_invalid_configuration") {
1963
1963
  this.#hasMalformedConfigRoot = false;
1964
- this.#hasInvalidNotificationConfiguration = true;
1964
+ this.#hasInvalidNotificationGlobal = true;
1965
1965
  return;
1966
1966
  }
1967
1967
  throw error;
@@ -1973,10 +1973,10 @@ export class Settings implements NotificationSettingsReader {
1973
1973
  try {
1974
1974
  parseNotificationSettingsSnapshot(this.#rawNotificationConfig);
1975
1975
  this.#hasMalformedConfigRoot = false;
1976
- this.#hasInvalidNotificationConfiguration = false;
1976
+ this.#hasInvalidNotificationGlobal = false;
1977
1977
  } catch (error) {
1978
1978
  if (error instanceof Error && error.message === "gjc_notify_daemon_invalid_configuration") {
1979
- this.#hasInvalidNotificationConfiguration = true;
1979
+ this.#hasInvalidNotificationGlobal = true;
1980
1980
  return;
1981
1981
  }
1982
1982
  throw error;
@@ -2054,9 +2054,9 @@ export class Settings implements NotificationSettingsReader {
2054
2054
  // Setting Hooks
2055
2055
  // ═══════════════════════════════════════════════════════════════════════════
2056
2056
 
2057
- type SettingHook<P extends SettingPath> = (value: SettingValue<P>, prev: SettingValue<P>) => void;
2057
+ type SettingHook = (value: unknown, prev: unknown) => void;
2058
2058
 
2059
- const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook<any>>> = {
2059
+ const SETTING_HOOKS: Partial<Record<SettingPath, SettingHook>> = {
2060
2060
  "theme.dark": value => {
2061
2061
  if (typeof value === "string") {
2062
2062
  setAutoThemeMapping("dark", value);
package/src/cursor.ts CHANGED
@@ -14,7 +14,6 @@ import type {
14
14
  ToolResultMessage,
15
15
  } from "@gajae-code/ai";
16
16
  import { sanitizeText } from "@gajae-code/utils";
17
- import { finalizeToolResultForDelivery } from "./tools/output-meta";
18
17
  import { resolveToCwd } from "./tools/path-utils";
19
18
 
20
19
  interface CursorExecBridgeOptions {
@@ -49,14 +48,6 @@ function buildToolErrorResult(message: string): AgentToolResult<unknown> {
49
48
  };
50
49
  }
51
50
 
52
- async function buildFinalToolErrorResult(
53
- options: CursorExecBridgeOptions,
54
- toolName: string,
55
- message: string,
56
- ): Promise<AgentToolResult<unknown>> {
57
- return finalizeToolResultForDelivery(buildToolErrorResult(message), toolName, options.getToolContext?.());
58
- }
59
-
60
51
  async function executeTool(
61
52
  options: CursorExecBridgeOptions,
62
53
  toolName: string,
@@ -65,12 +56,11 @@ async function executeTool(
65
56
  ): Promise<ToolResultMessage> {
66
57
  const tool = options.tools.get(toolName);
67
58
  if (!tool) {
68
- const result = await buildFinalToolErrorResult(options, toolName, `Tool "${toolName}" not available`);
59
+ const result = buildToolErrorResult(`Tool "${toolName}" not available`);
69
60
  return createToolResultMessage(toolCallId, toolName, result, true);
70
61
  }
71
62
 
72
63
  options.emitEvent?.({ type: "tool_execution_start", toolCallId, toolName, args });
73
- const context = options.getToolContext?.();
74
64
 
75
65
  let result: AgentToolResult<unknown>;
76
66
  let isError = false;
@@ -92,10 +82,16 @@ async function executeTool(
92
82
  : undefined;
93
83
 
94
84
  try {
95
- result = await tool.execute(toolCallId, args as Record<string, unknown>, undefined, onUpdate, context);
85
+ result = await tool.execute(
86
+ toolCallId,
87
+ args as Record<string, unknown>,
88
+ undefined,
89
+ onUpdate,
90
+ options.getToolContext?.(),
91
+ );
96
92
  } catch (error) {
97
93
  const message = error instanceof Error ? error.message : String(error);
98
- result = await buildFinalToolErrorResult(options, toolName, message);
94
+ result = buildToolErrorResult(message);
99
95
  isError = true;
100
96
  }
101
97
 
@@ -137,7 +133,6 @@ async function executeDelete(options: CursorExecBridgeOptions, pathArg: string,
137
133
  result = buildToolErrorResult(message);
138
134
  isError = true;
139
135
  }
140
- result = await finalizeToolResultForDelivery(result, toolName, options.getToolContext?.());
141
136
 
142
137
  options.emitEvent?.({ type: "tool_execution_end", toolCallId, toolName, result, isError });
143
138
  return createToolResultMessage(toolCallId, toolName, result, isError);
@@ -227,9 +222,7 @@ export class CursorExecHandlers implements ICursorExecHandlers {
227
222
  const globPath = `${args.path || "."}/${args.glob}`;
228
223
  return executeTool(this.#optionsForCall(), "find", toolCallId, { paths: [globPath] });
229
224
  }
230
- const result = await buildFinalToolErrorResult(
231
- this.#optionsForCall(),
232
- "search",
225
+ const result = buildToolErrorResult(
233
226
  "Cursor grep request rejected: pattern must not be empty. Provide a non-empty search pattern.",
234
227
  );
235
228
  return createToolResultMessage(toolCallId, "search", result, true);
@@ -279,7 +272,7 @@ export class CursorExecHandlers implements ICursorExecHandlers {
279
272
  const toolName = "bash";
280
273
  const tool = options.tools.get(toolName);
281
274
  if (!tool) {
282
- const result = await buildFinalToolErrorResult(options, toolName, `Tool "${toolName}" not available`);
275
+ const result = buildToolErrorResult(`Tool "${toolName}" not available`);
283
276
  return createToolResultMessage(toolCallId, toolName, result, true);
284
277
  }
285
278
 
@@ -338,7 +331,7 @@ export class CursorExecHandlers implements ICursorExecHandlers {
338
331
  result = await tool.execute(toolCallId, toolArgs, undefined, onUpdate, options.getToolContext?.());
339
332
  } catch (error) {
340
333
  const message = error instanceof Error ? error.message : String(error);
341
- result = await buildFinalToolErrorResult(options, toolName, message);
334
+ result = buildToolErrorResult(message);
342
335
  isError = true;
343
336
  }
344
337
 
@@ -388,7 +381,7 @@ export class CursorExecHandlers implements ICursorExecHandlers {
388
381
  if (!tool) {
389
382
  const availableTools = Array.from(options.tools.keys()).filter(name => name.startsWith("mcp__"));
390
383
  const message = formatMcpToolErrorMessage(toolName, availableTools);
391
- const result = await buildFinalToolErrorResult(options, toolName, message);
384
+ const result = buildToolErrorResult(message);
392
385
  return createToolResultMessage(toolCallId, toolName, result, true);
393
386
  }
394
387
 
@@ -295,8 +295,7 @@ The heavyweight gate runs **once per boundary generation**, not once per story a
295
295
  One generation freezes the change set and reviews it exactly once:
296
296
 
297
297
  1. Run implementation verification for the boundary's cumulative change set.
298
- 2. **Freeze the change set.** Use the native coordinator: run `gjc ultragoal review-source freeze --json` before dispatch, then create each lane with `gjc ultragoal review-source dispatch --cohort-id <id> --task-id <id> --lane <cleaner|architect|qa|critic> --rerun-command <command> --json` and pass the returned leader-issued `reviewSource` object to `task`. The runtime derives and persists the canonical source identity; never hand-author or copy a `sourceHash`. Every delivered lane receipt is classified `current`, `stale_review_delivery`, or `invalid_provenance` against the active source, and only current runtime-owned delivery IDs may enter the gate.
299
-
298
+ 2. **Freeze the change set.** Compute one immutable `sourceHash` over the reviewed source. Every lane in this generation inspects that same frozen snapshot; a lane verdict carrying a different `sourceHash` is rejected.
300
299
  3. **Run the cohort lanes on the frozen snapshot** — at most one `cleaner`, one `architect`, and one `qa` lane per generation. They may run in parallel because they share the frozen source; a second architect or QA lane in the same generation is rejected. The `cleaner` lane is the internal ai-slop-cleaner skill fragment run over the frozen change set: a read-only detector that emits an `AI SLOP CLEANUP REPORT`, and it still runs and records a passed/no-op report when there are no relevant edits. Its BLOCKING findings join the cohort findings rather than starting their own fix loop; advisory findings are included in the gate report only and are not written to the Ultragoal ledger.
301
300
  4. Delegate an `architect` review covering all three lanes:
302
301
  - architecture-side: system boundaries, layering, data/control flow, operational risks.
@@ -310,7 +309,7 @@ One generation freezes the change set and reviews it exactly once:
310
309
  - API/package surfaces require a real artifact file or typed receipt whose artifact `kind` contains one of `api`, `package`, `consumer`, `black-box`, or `test-report`; examples: `api-package-test-report`, `package-consumer-report`, `black-box-api-receipt`. Algorithm/math surfaces require a real artifact file or typed receipt whose artifact `kind` contains one of `property`, `boundary`, `edge`, `adversarial`, `failure`, `math`, `algorithm`, or `test-report`; examples: `property-test-report`, `algorithm-boundary-report`. Bare `inlineEvidence` text alone is not sufficient for any surface.
311
310
  - The mandatory **computer-use** red-team suite (`kill-switch-bypass`, `suspended-enforcement`, `permission-revoked`, …) is conditional, not universal: require it only when computer/desktop control is genuinely part of the product surface being dogfooded. For every other product type, prove the change through the matching live surface instead — browser-use automation for web/GUI, bash/CLI live invocation or argv replay for CLI, and real artifacts or typed receipts for API/package/algorithm/math. Editing docs, prompts, or skills that merely mention computer-use does not by itself make the computer-use suite applicable; pick the red-team surface that matches what the change actually ships.
312
311
  7. The executor QA/red-team lane must report a matrix using `executorQa.contractCoverage`, `executorQa.surfaceEvidence`, `executorQa.adversarialCases`, and `executorQa.artifactRefs`. Not-applicable rows are allowed only in `contractCoverage` and `surfaceEvidence`; each `status: "not_applicable"` row requires `contractRef` plus `reason`. `adversarialCases` rows cannot be not-applicable.
313
- 8. **Join before repairing.** Fold all three lane verdicts and the final code review into the strict gate under `iteration.reviewCohort` (`cohortId`, `reviewGeneration`, runtime-derived `sourceHash`, `joined: true`, and the three lanes with runtime-issued `deliveryId`). No lane may checkpoint on its own, and no fix work starts until the findings are joined. A `stale_review_delivery` stays visible for audit, is excluded from approval and blocker authority, and must be rerun using the rendered command. Clean means `architectReview.architectureStatus`, `architectReview.productStatus`, and `architectReview.codeStatus` are all `"CLEAR"`, `architectReview.recommendation` is `"APPROVE"`, executor QA statuses are `"passed"`, iteration is `"passed"` with `fullRerun: true`, the cohort is joined with every lane clean and receipt-bound to the active snapshot, every evidence field is non-empty, every required matrix row is present, and every blockers array is empty. `COMMENT`, `WATCH`, `REQUEST CHANGES`, `BLOCK`, stale/invalid provenance, missing evidence, missing or shallow matrix rows, plan/code mismatches, or non-empty blockers are non-clean.
312
+ 8. **Join before repairing.** Fold all three lane verdicts and the final code review into the strict gate under `iteration.reviewCohort` (`reviewGeneration`, `sourceHash`, `joined: true`, and the three `lanes`). No lane may checkpoint on its own, and no fix work starts until the findings are joined. Clean means `architectReview.architectureStatus`, `architectReview.productStatus`, and `architectReview.codeStatus` are all `"CLEAR"`, `architectReview.recommendation` is `"APPROVE"`, executor QA statuses are `"passed"`, iteration is `"passed"` with `fullRerun: true`, the cohort is joined with every lane clean and hash-bound, every evidence field is non-empty, every required matrix row is present, and every blockers array is empty. `COMMENT`, `WATCH`, `REQUEST CHANGES`, `BLOCK`, missing evidence, missing or shallow matrix rows, plan/code mismatches, or non-empty blockers are non-clean.
314
313
  9. If the joined findings contain any blocker, do **not** checkpoint `complete` and do **not** call `goal({"op":"complete"})`. Record **one consolidated blocker batch** for all findings from the whole cohort instead of one story per lane:
315
314
  ```sh
316
315
  gjc ultragoal record-review-blockers --goal-id <id> --title "Resolve verification blockers" --objective "<blocker-resolution objective>" --evidence "<joined cohort findings>"
@@ -11,8 +11,8 @@ export interface ExecutorBackendExecOptions {
11
11
  session: ToolSession;
12
12
  deadlineMs: number;
13
13
  reset: boolean;
14
- artifactPath?: string;
15
- artifactId?: string;
14
+ artifactPath: string | undefined;
15
+ artifactId: string | undefined;
16
16
  onChunk: (chunk: string) => void;
17
17
  }
18
18