@gajae-code/coding-agent 0.11.9 → 0.11.11

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 +56 -1
  2. package/dist/types/cli.d.ts +8 -1
  3. package/dist/types/config/model-profile-activation.d.ts +1 -0
  4. package/dist/types/config/model-profile-contract.d.ts +2 -0
  5. package/dist/types/config/model-registry.d.ts +2 -0
  6. package/dist/types/config/settings-schema.d.ts +57 -2
  7. package/dist/types/config/settings.d.ts +1 -0
  8. package/dist/types/extensibility/extensions/types.d.ts +21 -0
  9. package/dist/types/gjc-runtime/linux-proc.d.ts +22 -14
  10. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +60 -0
  11. package/dist/types/gjc-runtime/state-runtime.d.ts +3 -1
  12. package/dist/types/gjc-runtime/state-writer.d.ts +1 -0
  13. package/dist/types/modes/acp/acp-agent.d.ts +14 -9
  14. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  15. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  16. package/dist/types/modes/components/welcome.d.ts +1 -0
  17. package/dist/types/modes/controllers/selector-controller.d.ts +16 -8
  18. package/dist/types/modes/interactive-mode.d.ts +5 -1
  19. package/dist/types/runtime/memory-domain.d.ts +9 -0
  20. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  21. package/dist/types/runtime/memory-guard.d.ts +39 -0
  22. package/dist/types/runtime/memory-limit.d.ts +11 -0
  23. package/dist/types/runtime-mcp/manager.d.ts +8 -0
  24. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  25. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  26. package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
  27. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -1
  28. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  29. package/dist/types/sdk/bus/index.d.ts +11 -2
  30. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  31. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  32. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +14 -7
  33. package/dist/types/sdk/bus/telegram-daemon.d.ts +97 -8
  34. package/dist/types/sdk/host/control/operations.d.ts +1 -0
  35. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  36. package/dist/types/sdk/session.d.ts +7 -0
  37. package/dist/types/sdk/startup-capability.d.ts +6 -0
  38. package/dist/types/session/agent-session.d.ts +11 -11
  39. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  40. package/dist/types/session/internal/managed-session-scope.d.ts +28 -1
  41. package/dist/types/session/session-manager.d.ts +34 -0
  42. package/dist/types/task/executor.d.ts +6 -0
  43. package/dist/types/task/provider-retry-status.d.ts +13 -0
  44. package/dist/types/task/types.d.ts +39 -0
  45. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  46. package/dist/types/tools/index.d.ts +3 -1
  47. package/dist/types/tools/resource-gc.d.ts +19 -0
  48. package/package.json +8 -8
  49. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  50. package/scripts/run-test-manifest.ts +6 -4
  51. package/src/cli/args.ts +14 -8
  52. package/src/cli/fast-help.ts +1 -1
  53. package/src/cli/notify-cli.ts +1 -0
  54. package/src/cli.ts +48 -2
  55. package/src/commands/sdk.ts +91 -7
  56. package/src/config/model-profile-activation.ts +1 -0
  57. package/src/config/model-profile-contract.ts +13 -0
  58. package/src/config/model-profiles.ts +31 -7
  59. package/src/config/model-registry.ts +4 -2
  60. package/src/config/settings-schema.ts +61 -2
  61. package/src/config/settings.ts +8 -0
  62. package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -2
  63. package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
  64. package/src/eval/js/tool-bridge.ts +1 -1
  65. package/src/extensibility/extensions/runner.ts +4 -0
  66. package/src/extensibility/extensions/types.ts +14 -0
  67. package/src/gjc-runtime/deep-interview-ambiguity.ts +16 -2
  68. package/src/gjc-runtime/deep-interview-draft.ts +2 -1
  69. package/src/gjc-runtime/deep-interview-recorder.ts +16 -1
  70. package/src/gjc-runtime/deep-interview-state.ts +25 -3
  71. package/src/gjc-runtime/linux-proc.ts +70 -36
  72. package/src/gjc-runtime/ralplan-runtime.ts +252 -1
  73. package/src/gjc-runtime/state-runtime.ts +48 -11
  74. package/src/gjc-runtime/state-writer.ts +1 -1
  75. package/src/hooks/skill-state.ts +23 -1
  76. package/src/internal-urls/docs-index.generated.ts +7 -7
  77. package/src/internal-urls/local-protocol.ts +23 -4
  78. package/src/modes/acp/acp-agent.ts +554 -60
  79. package/src/modes/acp/acp-event-mapper.ts +93 -6
  80. package/src/modes/components/model-selector.ts +15 -1
  81. package/src/modes/components/notifications-settings-editor.ts +1 -1
  82. package/src/modes/components/queue-pane.ts +55 -9
  83. package/src/modes/components/queued-message-selector.ts +53 -10
  84. package/src/modes/components/tool-status-header.ts +1 -4
  85. package/src/modes/components/welcome.ts +18 -3
  86. package/src/modes/controllers/command-controller.ts +4 -0
  87. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  88. package/src/modes/controllers/input-controller.ts +44 -3
  89. package/src/modes/controllers/selector-controller.ts +38 -8
  90. package/src/modes/interactive-mode.ts +65 -26
  91. package/src/modes/runtime-init.ts +1 -0
  92. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  93. package/src/prompts/agents/architect.md +8 -1
  94. package/src/prompts/agents/critic.md +8 -1
  95. package/src/prompts/agents/executor.md +1 -1
  96. package/src/prompts/agents/planner.md +8 -1
  97. package/src/runtime/memory-domain.ts +66 -0
  98. package/src/runtime/memory-guard-contract.ts +73 -0
  99. package/src/runtime/memory-guard.ts +292 -0
  100. package/src/runtime/memory-limit.ts +50 -0
  101. package/src/runtime-mcp/manager.ts +26 -6
  102. package/src/sdk/acp/adapter.ts +8 -1
  103. package/src/sdk/acp/final-text.ts +40 -0
  104. package/src/sdk/acp/mcp.ts +26 -0
  105. package/src/sdk/broker/identity.ts +22 -12
  106. package/src/sdk/broker/lifecycle.ts +82 -1
  107. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  108. package/src/sdk/bus/chat-daemon-control.ts +4 -3
  109. package/src/sdk/bus/config.ts +1 -1
  110. package/src/sdk/bus/daemon-paths.ts +2 -0
  111. package/src/sdk/bus/index.ts +222 -27
  112. package/src/sdk/bus/notification-service.ts +19 -5
  113. package/src/sdk/bus/operator-runtime.ts +31 -5
  114. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  115. package/src/sdk/bus/telegram-cli.ts +6 -13
  116. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  117. package/src/sdk/bus/telegram-daemon-contract.ts +15 -7
  118. package/src/sdk/bus/telegram-daemon-control.ts +14 -5
  119. package/src/sdk/bus/telegram-daemon.ts +1191 -164
  120. package/src/sdk/host/control/dispatch.ts +2 -0
  121. package/src/sdk/host/control/operations.ts +1 -0
  122. package/src/sdk/host/host.ts +2 -2
  123. package/src/sdk/lifecycle-session.ts +19 -3
  124. package/src/sdk/protocol/operation-inventory.generated.json +58 -0
  125. package/src/sdk/protocol/operation-registry.ts +1 -0
  126. package/src/sdk/session.ts +24 -1
  127. package/src/sdk/startup-capability.ts +6 -0
  128. package/src/session/agent-session.ts +290 -132
  129. package/src/session/fallback-chain-controller.ts +47 -8
  130. package/src/session/internal/managed-session-scope.ts +173 -127
  131. package/src/session/internal/native-publish-outcome.ts +8 -3
  132. package/src/session/session-manager.ts +608 -25
  133. package/src/skill-state/workflow-mutation-guard.ts +41 -7
  134. package/src/task/executor.ts +152 -42
  135. package/src/task/index.ts +2 -0
  136. package/src/task/provider-retry-status.ts +88 -0
  137. package/src/task/render.ts +45 -26
  138. package/src/task/types.ts +25 -11
  139. package/src/task/ultragoal-redteam-activation.ts +75 -0
  140. package/src/tools/bash-allowed-prefixes.ts +45 -1
  141. package/src/tools/index.ts +3 -1
  142. package/src/tools/resource-gc.ts +496 -120
  143. package/src/tools/subagent-render.ts +74 -16
  144. package/src/tools/subagent.ts +2 -0
  145. package/src/workflow/workflow-intent-diff.ts +4 -1
  146. package/vendor/insane-search/MANIFEST.json +4 -1
  147. package/vendor/insane-search/engine/bias_check.py +5 -0
  148. package/vendor/insane-search/engine/fetch_chain.py +67 -1
  149. package/vendor/insane-search/engine/transport.py +13 -2
  150. package/vendor/insane-search/engine/validators.py +4 -1
@@ -64,8 +64,10 @@ import {
64
64
  import type { NotificationSessionStatus } from "../../sdk/bus/session-control";
65
65
  import {
66
66
  ensureTelegramDaemonRunningDetailed,
67
+ readNotificationRootRegistration,
67
68
  resolveTelegramSetupPreflight,
68
69
  unregisterNotificationRoot,
70
+ withNotificationRootRegistryFence,
69
71
  } from "../../sdk/bus/telegram-daemon";
70
72
  import { TelegramDaemonController } from "../../sdk/bus/telegram-daemon-control";
71
73
  import { runTelegramSetup, type TelegramSetupPreflight } from "../../sdk/bus/telegram-setup";
@@ -216,6 +218,12 @@ export interface NotificationsEditorAdapterContext {
216
218
  notifyConfigChanged?: () => Promise<void> | void;
217
219
  }
218
220
 
221
+ type TelegramDaemonStopResult = {
222
+ ok: boolean;
223
+ message: string;
224
+ before?: { health?: string };
225
+ };
226
+
219
227
  export interface NotificationsEditorOperationDependencies {
220
228
  getNotificationConfig: typeof getNotificationConfig;
221
229
  maskToken: typeof maskToken;
@@ -231,14 +239,17 @@ export interface NotificationsEditorOperationDependencies {
231
239
  reconcileCommittedTelegramConfiguration: typeof reconcileCommittedTelegramConfiguration;
232
240
  saveTelegramInactive: typeof saveTelegramInactive;
233
241
  removeTelegramConfiguration: typeof removeTelegramConfiguration;
242
+ readNotificationRootRegistration: typeof readNotificationRootRegistration;
234
243
  unregisterNotificationRoot: typeof unregisterNotificationRoot;
244
+ stopTelegramDaemonIfRootRegistryFenceMatches(input: {
245
+ settings: Settings;
246
+ registryFingerprint: string;
247
+ stop: () => Promise<TelegramDaemonStopResult>;
248
+ }): Promise<boolean>;
249
+
235
250
  reloadTelegramDaemon(settings: Settings): Promise<{ ok: boolean; message: string }>;
236
251
  restartTelegramDaemon(settings: Settings): Promise<{ ok: boolean; message: string }>;
237
- stopTelegramDaemon(settings: Settings): Promise<{
238
- ok: boolean;
239
- message: string;
240
- before?: { health?: string };
241
- }>;
252
+ stopTelegramDaemon(settings: Settings): Promise<TelegramDaemonStopResult>;
242
253
  }
243
254
 
244
255
  const notificationEditorOperationDependencies: NotificationsEditorOperationDependencies = {
@@ -256,7 +267,18 @@ const notificationEditorOperationDependencies: NotificationsEditorOperationDepen
256
267
  reconcileCommittedTelegramConfiguration,
257
268
  saveTelegramInactive,
258
269
  removeTelegramConfiguration,
270
+ readNotificationRootRegistration,
259
271
  unregisterNotificationRoot,
272
+ stopTelegramDaemonIfRootRegistryFenceMatches: async input =>
273
+ await withNotificationRootRegistryFence({
274
+ settings: input.settings,
275
+ registryFingerprint: input.registryFingerprint,
276
+ action: async () => {
277
+ const stopped = await input.stop();
278
+ if (!stopped.ok) throw new Error(stopped.message);
279
+ },
280
+ }),
281
+
260
282
  reloadTelegramDaemon: async settings =>
261
283
  await new TelegramDaemonController(settings).reload({ spawnIfStopped: false }),
262
284
  restartTelegramDaemon: async settings =>
@@ -625,14 +647,22 @@ export function createNotificationsEditorOperations(
625
647
  stopAndUnregister: async () => {
626
648
  if (controller) await controller.enterBlockedRuntime(sessionContext());
627
649
  runtimePrepared = true;
650
+ const registration = await services.readNotificationRootRegistration({
651
+ settings: ctx.settings,
652
+ sessionId: ctx.sessionManager.getSessionId(),
653
+ });
628
654
  const unregistered = await services.unregisterNotificationRoot({
629
655
  settings: ctx.settings,
630
656
  cwd: ctx.sessionManager.getCwd(),
631
657
  sessionId: ctx.sessionManager.getSessionId(),
658
+ registrationToken: registration.token,
632
659
  });
633
- if (unregistered.remainingRoots === 0) {
634
- const stopped = await new TelegramDaemonController(ctx.settings).stop();
635
- if (!stopped.ok) throw new Error(stopped.message);
660
+ if (unregistered.registryFingerprint !== undefined) {
661
+ await services.stopTelegramDaemonIfRootRegistryFenceMatches({
662
+ settings: ctx.settings,
663
+ registryFingerprint: unregistered.registryFingerprint,
664
+ stop: () => services.stopTelegramDaemon(ctx.settings),
665
+ });
636
666
  }
637
667
  },
638
668
  },
@@ -18,6 +18,7 @@ import chalk from "chalk";
18
18
  import { AsyncJobManager } from "../async";
19
19
  import {
20
20
  type AppKeybinding,
21
+ defaultMessageQueueKeysForPlatform,
21
22
  formatKeyHint,
22
23
  formatKeyHints,
23
24
  KeybindingsManager,
@@ -47,6 +48,7 @@ import type { SessionContext, SessionManager } from "../session/session-manager"
47
48
  import { getRecentSessions, getSessionMessageEntryId } from "../session/session-manager";
48
49
  import type { LspStartupServerInfo } from "../tools";
49
50
  import { formatPhaseDisplayName } from "../tools/todo-write";
51
+ import { copyToClipboard } from "../utils/clipboard";
50
52
  import type { EventBus } from "../utils/event-bus";
51
53
  import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
52
54
  import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
@@ -113,16 +115,60 @@ import {
113
115
  import type { ParsedIrcMessage } from "./utils/irc-message";
114
116
  import { addChatChild, prepareTranscriptRebuild, UiHelpers } from "./utils/ui-helpers";
115
117
 
116
- export function getDefaultComposerPlaceholder(context: KeyDisplayContext = { platform: process.platform }): string {
118
+ function buildComposerPlaceholder(
119
+ keybindings: Pick<KeybindingsManager, "getDisplayString">,
120
+ context: KeyDisplayContext,
121
+ options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
122
+ ): string {
123
+ const parts: string[] = [];
124
+ const submitKey = options.busy ? keybindings.getDisplayString("tui.input.submit", context) : "";
125
+ if (submitKey) {
126
+ const submitAction = options.busyPromptMode === "steer" ? "Steer" : "Queue";
127
+ parts.push(`${submitKey}: ${submitAction}`);
128
+ }
129
+
130
+ const queueKey = keybindings.getDisplayString("app.message.queue", context);
131
+ const submitQueues = options.busy && options.busyPromptMode === "queue" && submitKey;
132
+ if (queueKey && !submitQueues) parts.push(`${queueKey}: ${options.busy ? "Queue" : "Queue (busy)"}`);
133
+
134
+ const actionHints = [
135
+ ["app.thinking.cycle", "Thinking"],
136
+ ["app.model.select", "Model"],
137
+ ["app.history.search", "History"],
138
+ ] as const;
139
+ for (const [id, label] of actionHints) {
140
+ const key = keybindings.getDisplayString(id, context);
141
+ if (key) parts.push(`${key}: ${label}`);
142
+ }
143
+
117
144
  const newlineKeys = context.platform === "win32" ? ["alt+enter", "ctrl+j"] : ["shift+enter", "ctrl+j"];
118
- return `Type your message... ${formatKeyHints(newlineKeys, context)}: New line · ${formatKeyHint(
119
- "ctrl+c",
120
- context,
121
- )}: Clear · ${formatKeyHint("ctrl+r", context)}: Search history · ${formatKeyHint("shift+tab", context)}: Reasoning`;
145
+ parts.push(`${formatKeyHints(newlineKeys, context)}: New line`, `${formatKeyHint("ctrl+c", context)}: Clear`);
146
+ return `Type your message... ${parts.join(" · ")}`;
147
+ }
148
+
149
+ export function getDefaultComposerPlaceholder(
150
+ context: KeyDisplayContext = { platform: process.platform },
151
+ keybindings?: Pick<KeybindingsManager, "getDisplayString">,
152
+ ): string {
153
+ const effectiveKeybindings =
154
+ keybindings ??
155
+ KeybindingsManager.inMemory({
156
+ "app.message.queue": defaultMessageQueueKeysForPlatform(context.platform),
157
+ });
158
+ return buildComposerPlaceholder(effectiveKeybindings, context, { busy: false, busyPromptMode: "steer" });
122
159
  }
123
160
 
124
161
  export const DEFAULT_COMPOSER_PLACEHOLDER = getDefaultComposerPlaceholder();
162
+
163
+ export function getComposerPlaceholder(
164
+ keybindings: Pick<KeybindingsManager, "getDisplayString">,
165
+ context: KeyDisplayContext,
166
+ options: { readonly busy: boolean; readonly busyPromptMode: "steer" | "queue" },
167
+ ): string {
168
+ return buildComposerPlaceholder(keybindings, context, options);
169
+ }
125
170
  const WELCOME_RESERVED_CONTAINER_CHILD_LIMIT = 8;
171
+ const COMPOSER_RIGHT_GUTTER_WIDTH = 1;
126
172
 
127
173
  const IRC_SIDEBAR_TOGGLE_SHADOWING_ACTIONS: readonly AppKeybinding[] = [
128
174
  "app.plan.toggle",
@@ -169,7 +215,7 @@ function configureDefaultComposerChrome(editor: CustomEditor): void {
169
215
  editor.setInputPrefix(getDefaultInputPrefix());
170
216
  editor.setPlaceholder(getDefaultComposerPlaceholder());
171
217
  editor.setPaddingX(1);
172
- editor.setRightGutterWidth(1);
218
+ editor.setRightGutterWidth(COMPOSER_RIGHT_GUTTER_WIDTH);
173
219
  editor.setTopBorder(undefined);
174
220
  }
175
221
 
@@ -510,6 +556,14 @@ export class InteractiveMode implements InteractiveModeContext {
510
556
 
511
557
  this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"), {
512
558
  enableMouse: settings.get("mouse.enabled"),
559
+ copySelection: async text => {
560
+ try {
561
+ await copyToClipboard(text);
562
+ this.showStatus("Selection copied to clipboard");
563
+ } catch (error) {
564
+ this.showError(`Failed to copy selection: ${error instanceof Error ? error.message : String(error)}`);
565
+ }
566
+ },
513
567
  });
514
568
  this.ui.setClearOnShrink(settings.get("clearOnShrink"));
515
569
  this.chatContainer = new Container();
@@ -660,6 +714,7 @@ export class InteractiveMode implements InteractiveModeContext {
660
714
  getViewportRows: () => this.ui.terminal.rows,
661
715
  getReservedBottomRows: getWelcomeReservedBottomRows,
662
716
  changelogMarkdown: this.#changelogMarkdown,
717
+ rightGutterWidth: COMPOSER_RIGHT_GUTTER_WIDTH,
663
718
  collapseChangelog: settings.get("collapseChangelog"),
664
719
  keyDisplayContext: this.#keyDisplayContext,
665
720
  },
@@ -1056,27 +1111,11 @@ export class InteractiveMode implements InteractiveModeContext {
1056
1111
  return this.session.isStreaming || this.session.isCompacting;
1057
1112
  }
1058
1113
 
1059
- #getFirstKeyForAction(action: AppKeybinding): string | undefined {
1060
- return this.keybindings.getKeys(action)[0];
1061
- }
1062
-
1063
- #getMessageQueueShortcut(): string | undefined {
1064
- const preferredAction: AppKeybinding =
1065
- process.platform === "darwin" ? "app.message.followUp" : "app.message.queue";
1066
- const fallbackAction: AppKeybinding =
1067
- process.platform === "darwin" ? "app.message.queue" : "app.message.followUp";
1068
- return this.#getFirstKeyForAction(preferredAction) ?? this.#getFirstKeyForAction(fallbackAction);
1069
- }
1070
-
1071
1114
  #getComposerPlaceholder(): string {
1072
- const defaultPlaceholder = getDefaultComposerPlaceholder(this.#keyDisplayContext);
1073
- if (!this.#isPromptDeliveryBusy()) return defaultPlaceholder;
1074
- const submitAction = this.settings.get("busyPromptMode") === "steer" ? "Steer" : "Queue";
1075
- const submitKey = this.keybindings.getDisplayString("tui.input.submit", this.#keyDisplayContext);
1076
- const parts = submitKey ? [`${submitKey}: ${submitAction}`] : [];
1077
- const queueKey = this.#getMessageQueueShortcut();
1078
- if (queueKey) parts.push(`${formatKeyHint(queueKey, this.#keyDisplayContext)}: Queue`);
1079
- return parts.length > 0 ? `${defaultPlaceholder} · ${parts.join(" · ")}` : defaultPlaceholder;
1115
+ return getComposerPlaceholder(this.keybindings, this.#keyDisplayContext, {
1116
+ busy: this.#isPromptDeliveryBusy(),
1117
+ busyPromptMode: this.settings.get("busyPromptMode"),
1118
+ });
1080
1119
  }
1081
1120
 
1082
1121
  #getWelcomeReservedRows(width: number): number {
@@ -115,6 +115,7 @@ export async function initializeExtensions(session: AgentSession, options: Initi
115
115
  compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
116
116
  clearContext: () => session.clearContext(),
117
117
  cycleModel: () => session.cycleModel(),
118
+ setModelProfile: name => session.activateModelProfileForControl(name),
118
119
  cycleThinkingLevel: () => session.cycleThinkingLevel(),
119
120
  setQueueMode: (kind, mode) => {
120
121
  if (kind === "steering" && (mode === "all" || mode === "one-at-a-time")) {
@@ -1 +1 @@
1
- - Exception: restricted `bash` is only for sanctioned GJC workflow persistence (`gjc ralplan --write ...`) and GJC workflow state commands (`gjc state ...`). Pass artifacts through `GJC_RALPLAN_ARTIFACT` with `--artifact-env GJC_RALPLAN_ARTIFACT`; never use bash for product-source writes or general shell work.
1
+ - Exception: restricted `bash` is only for sanctioned GJC workflow persistence (`gjc ralplan --write ...`), GJC workflow state commands (`gjc state ...`), and read-only git inspection (`git status`, `git log`, `git show`, `git diff`, `git blame`, `git rev-parse`, `git ls-files`). Pass artifacts through `GJC_RALPLAN_ARTIFACT` with `--artifact-env GJC_RALPLAN_ARTIFACT`; never use bash for product-source writes, commits, pushes, or other mutating/general shell work.
@@ -1,13 +1,20 @@
1
1
  ---
2
2
  name: architect
3
3
  description: Read-only architecture and code-review agent with severity-rated findings and status verdicts
4
- tools: read, search, find, lsp, ast_grep, web_search, bash, report_finding
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash, report_finding, irc
5
5
  thinking-level: high
6
6
  blocking: true
7
7
  forkContext: allowed
8
8
  bashAllowedPrefixes:
9
9
  - gjc ralplan --write
10
10
  - gjc state
11
+ - git status
12
+ - git log
13
+ - git show
14
+ - git diff
15
+ - git blame
16
+ - git rev-parse
17
+ - git ls-files
11
18
  ---
12
19
  <identity>
13
20
  You are Architect. You combine system architecture review with code-review discipline. Diagnose, analyze, and recommend with file-backed evidence. You are read-only.
@@ -1,11 +1,18 @@
1
1
  ---
2
2
  name: critic
3
3
  description: Read-only plan critic that approves only actionable, verifiable execution plans
4
- tools: read, search, find, lsp, ast_grep, web_search, bash
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash, irc
5
5
  thinking-level: high
6
6
  bashAllowedPrefixes:
7
7
  - gjc ralplan --write
8
8
  - gjc state
9
+ - git status
10
+ - git log
11
+ - git show
12
+ - git diff
13
+ - git blame
14
+ - git rev-parse
15
+ - git ls-files
9
16
  ---
10
17
  <identity>
11
18
  You are Critic. Decide whether a work plan is actionable before execution begins.
@@ -34,7 +34,7 @@ Explore just enough context, implement the smallest correct change, and leave co
34
34
  {{#if ultragoalRedTeam}}
35
35
 
36
36
  <ultragoal_red_team_mode>
37
- This mode activates only when the assignment explicitly labels Executor as Ultragoal completion QA/red-team or asks for `executorQa` red-team evidence. Otherwise, preserve ordinary Executor behavior.
37
+ This mode activates only when the task sets typed `executionMode: "ultragoal-red-team"`, or — as a compatibility fallback — when the assignment text explicitly labels Executor as Ultragoal completion QA/red-team or asks for `executorQa` red-team evidence (not a bare `executorQa` field-name mention). Prefer the typed flag. Otherwise, preserve ordinary Executor behavior.
38
38
 
39
39
  When active:
40
40
  - Follow the exact `executorQa` contract provided in the assignment (matrix shape, row fields, artifact/replay rules); the runtime validates it strictly. If the assignment omits the contract, read the ultragoal SKILL's executor QA section before producing evidence.
@@ -1,11 +1,18 @@
1
1
  ---
2
2
  name: planner
3
3
  description: Read-only planning agent for sequencing, acceptance criteria, risks, and handoff shape
4
- tools: read, search, find, lsp, ast_grep, web_search, bash
4
+ tools: read, search, find, lsp, ast_grep, web_search, bash, irc
5
5
  thinking-level: medium
6
6
  bashAllowedPrefixes:
7
7
  - gjc ralplan --write
8
8
  - gjc state
9
+ - git status
10
+ - git log
11
+ - git show
12
+ - git diff
13
+ - git blame
14
+ - git rev-parse
15
+ - git ls-files
9
16
  ---
10
17
  <identity>
11
18
  You are Planner. Turn requests into actionable work plans. You plan; you do not implement.
@@ -0,0 +1,66 @@
1
+ import type {
2
+ MemoryGuardDomainSnapshot,
3
+ MemoryGuardWorkerAccounting,
4
+ MemoryGuardWorkerSample,
5
+ } from "./memory-guard-contract";
6
+
7
+ export interface MemoryGuardDomainInput {
8
+ effectiveLimitBytes: number;
9
+ totalUsageBytes: number;
10
+ parentBytes: number;
11
+ parentReserveBytes: number;
12
+ workers: readonly MemoryGuardWorkerSample[];
13
+ }
14
+
15
+ function assertNonNegativeSafeInteger(name: string, value: number): number {
16
+ if (!Number.isSafeInteger(value) || value < 0) throw new Error(`invalid_${name}`);
17
+ return value;
18
+ }
19
+
20
+ function sumWorkerBytes(workers: readonly MemoryGuardWorkerSample[]): number {
21
+ let total = 0;
22
+ for (const worker of workers) {
23
+ total += assertNonNegativeSafeInteger(`worker_bytes:${worker.workerId}`, worker.bytes);
24
+ if (!Number.isSafeInteger(total)) throw new Error("invalid_worker_total_bytes");
25
+ }
26
+ return total;
27
+ }
28
+
29
+ export function computeMemoryGuardDomain(input: MemoryGuardDomainInput): MemoryGuardDomainSnapshot {
30
+ const effectiveLimitBytes = assertNonNegativeSafeInteger("effective_limit_bytes", input.effectiveLimitBytes);
31
+ const totalUsageBytes = assertNonNegativeSafeInteger("total_usage_bytes", input.totalUsageBytes);
32
+ const parentBytes = assertNonNegativeSafeInteger("parent_bytes", input.parentBytes);
33
+ const parentReserveBytes = assertNonNegativeSafeInteger("parent_reserve_bytes", input.parentReserveBytes);
34
+ const totalWorkerBytes = sumWorkerBytes(input.workers);
35
+ const acceptedWorkers = input.workers.filter(worker => worker.accepted !== false);
36
+ const acceptedWorkerBytes = sumWorkerBytes(acceptedWorkers);
37
+ const acceptedWorkerCount = acceptedWorkers.length;
38
+ const unmanagedBytes = Math.max(0, totalUsageBytes - parentBytes - acceptedWorkerBytes);
39
+ const headroomBytes = Math.max(0, effectiveLimitBytes - totalUsageBytes);
40
+ const workerBudgetBytes = Math.max(0, effectiveLimitBytes - unmanagedBytes - parentReserveBytes);
41
+ const perWorkerAllowanceBytes = acceptedWorkerCount === 0 ? 0 : workerBudgetBytes / acceptedWorkerCount;
42
+ const workers: MemoryGuardWorkerAccounting[] = input.workers.map(worker => {
43
+ const accepted = worker.accepted !== false;
44
+ return {
45
+ workerId: worker.workerId,
46
+ bytes: worker.bytes,
47
+ accepted,
48
+ allowanceBytes: accepted ? perWorkerAllowanceBytes : 0,
49
+ excessBytes: accepted ? Math.max(0, worker.bytes - perWorkerAllowanceBytes) : 0,
50
+ };
51
+ });
52
+ return {
53
+ effectiveLimitBytes,
54
+ totalUsageBytes,
55
+ parentBytes,
56
+ parentReserveBytes,
57
+ totalWorkerBytes,
58
+ acceptedWorkerCount,
59
+ unmanagedBytes,
60
+ headroomBytes,
61
+ workerBudgetBytes,
62
+ perWorkerAllowanceBytes,
63
+ hostExcessBytes: Math.max(0, parentBytes - parentReserveBytes),
64
+ workers,
65
+ };
66
+ }
@@ -0,0 +1,73 @@
1
+ export interface MemoryGuardPolicy {
2
+ enabled: boolean;
3
+ checkIntervalMs: number;
4
+ gcThresholdRatio: number;
5
+ restartThresholdRatio: number;
6
+ restartThresholdWindowMs: number;
7
+ cooldownMs: number;
8
+ parentReserveBytes: number;
9
+ policyLimitBytes: number | null;
10
+ }
11
+
12
+ export interface MemoryGuardWorkerSample {
13
+ workerId: string;
14
+ bytes: number;
15
+ accepted?: boolean;
16
+ }
17
+
18
+ export interface MemoryGuardWorkerAccounting {
19
+ workerId: string;
20
+ bytes: number;
21
+ accepted: boolean;
22
+ allowanceBytes: number;
23
+ excessBytes: number;
24
+ }
25
+
26
+ export interface MemoryGuardDomainSnapshot {
27
+ effectiveLimitBytes: number;
28
+ totalUsageBytes: number;
29
+ parentBytes: number;
30
+ parentReserveBytes: number;
31
+ totalWorkerBytes: number;
32
+ acceptedWorkerCount: number;
33
+ unmanagedBytes: number;
34
+ headroomBytes: number;
35
+ workerBudgetBytes: number;
36
+ perWorkerAllowanceBytes: number;
37
+ hostExcessBytes: number;
38
+ workers: MemoryGuardWorkerAccounting[];
39
+ }
40
+
41
+ export interface MemoryGuardHostTarget {
42
+ kind: "host";
43
+ excessBytes: number;
44
+ }
45
+
46
+ export interface MemoryGuardWorkerTarget {
47
+ kind: "worker";
48
+ workerId: string;
49
+ excessBytes: number;
50
+ }
51
+
52
+ export type MemoryGuardActionTarget = MemoryGuardHostTarget | MemoryGuardWorkerTarget;
53
+
54
+ export type MemoryGuardNoopReason =
55
+ | "memory_guard_action_noop_unmanaged_or_within_allowance"
56
+ | "memory_guard_action_noop_unsupported";
57
+
58
+ export type MemoryGuardDecision =
59
+ | { kind: "execute"; target: MemoryGuardActionTarget }
60
+ | { kind: "noop"; reason: MemoryGuardNoopReason }
61
+ | { kind: "revalidated_out"; reason: "memory_guard_action_revalidated_out" };
62
+
63
+ export interface MemoryGuardSchedulerState {
64
+ timerActive: boolean;
65
+ registrationCount: number;
66
+ inProgress: boolean;
67
+ generation: number;
68
+ pendingDeadline: number | null;
69
+ pendingOwner: { generation: number; token: number } | null;
70
+ deferredDeadline: number | null;
71
+ deferredGeneration: number | null;
72
+ activeGeneration: number | null;
73
+ }