@gajae-code/coding-agent 0.2.5 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/types/async/job-manager.d.ts +91 -2
  3. package/dist/types/cli/args.d.ts +1 -1
  4. package/dist/types/commands/deep-interview.d.ts +3 -0
  5. package/dist/types/commands/harness.d.ts +37 -0
  6. package/dist/types/config/keybindings.d.ts +5 -0
  7. package/dist/types/config/settings-schema.d.ts +10 -4
  8. package/dist/types/config/settings.d.ts +2 -0
  9. package/dist/types/debug/crash-diagnostics.d.ts +45 -0
  10. package/dist/types/debug/runtime-gauges.d.ts +6 -0
  11. package/dist/types/deep-interview/render-middleware.d.ts +6 -0
  12. package/dist/types/eval/py/executor.d.ts +2 -0
  13. package/dist/types/eval/py/kernel.d.ts +2 -0
  14. package/dist/types/exec/bash-executor.d.ts +10 -0
  15. package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
  16. package/dist/types/extensibility/extensions/types.d.ts +6 -0
  17. package/dist/types/extensibility/shared-events.d.ts +1 -0
  18. package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
  19. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
  20. package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
  21. package/dist/types/gjc-runtime/state-migrations.d.ts +33 -0
  22. package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
  23. package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
  24. package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
  25. package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
  26. package/dist/types/gjc-runtime/state-writer.d.ts +147 -0
  27. package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
  28. package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
  29. package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
  30. package/dist/types/harness-control-plane/classifier.d.ts +13 -0
  31. package/dist/types/harness-control-plane/control-endpoint.d.ts +31 -0
  32. package/dist/types/harness-control-plane/finalize.d.ts +47 -0
  33. package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
  34. package/dist/types/harness-control-plane/operate.d.ts +35 -0
  35. package/dist/types/harness-control-plane/owner.d.ts +46 -0
  36. package/dist/types/harness-control-plane/preserve.d.ts +19 -0
  37. package/dist/types/harness-control-plane/receipts.d.ts +88 -0
  38. package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
  39. package/dist/types/harness-control-plane/seams.d.ts +21 -0
  40. package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
  41. package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
  42. package/dist/types/harness-control-plane/storage.d.ts +53 -0
  43. package/dist/types/harness-control-plane/types.d.ts +162 -0
  44. package/dist/types/hooks/skill-keywords.d.ts +2 -1
  45. package/dist/types/hooks/skill-state.d.ts +23 -29
  46. package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
  47. package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
  48. package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
  49. package/dist/types/internal-urls/types.d.ts +4 -0
  50. package/dist/types/lsp/index.d.ts +10 -10
  51. package/dist/types/modes/bridge/auth.d.ts +12 -0
  52. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
  53. package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
  54. package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
  55. package/dist/types/modes/bridge/event-stream.d.ts +8 -0
  56. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  57. package/dist/types/modes/components/hook-selector.d.ts +1 -0
  58. package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
  59. package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
  60. package/dist/types/modes/components/status-line/types.d.ts +2 -0
  61. package/dist/types/modes/components/status-line.d.ts +2 -0
  62. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  63. package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
  64. package/dist/types/modes/index.d.ts +1 -0
  65. package/dist/types/modes/interactive-mode.d.ts +2 -0
  66. package/dist/types/modes/jobs-observer.d.ts +57 -0
  67. package/dist/types/modes/rpc/host-tools.d.ts +1 -16
  68. package/dist/types/modes/rpc/host-uris.d.ts +1 -38
  69. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
  70. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
  71. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
  72. package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
  73. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
  74. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
  75. package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
  76. package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
  77. package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
  78. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
  79. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
  80. package/dist/types/modes/types.d.ts +2 -0
  81. package/dist/types/sdk.d.ts +4 -0
  82. package/dist/types/session/agent-session.d.ts +19 -1
  83. package/dist/types/skill-state/active-state.d.ts +2 -0
  84. package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
  85. package/dist/types/skill-state/workflow-state-contract.d.ts +25 -2
  86. package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
  87. package/dist/types/task/executor.d.ts +3 -0
  88. package/dist/types/task/id.d.ts +7 -0
  89. package/dist/types/task/index.d.ts +5 -0
  90. package/dist/types/task/receipt.d.ts +85 -0
  91. package/dist/types/task/spawn-gate.d.ts +38 -0
  92. package/dist/types/task/types.d.ts +198 -14
  93. package/dist/types/tools/cron.d.ts +6 -0
  94. package/dist/types/tools/index.d.ts +2 -0
  95. package/dist/types/tools/path-utils.d.ts +1 -0
  96. package/dist/types/tools/subagent.d.ts +26 -1
  97. package/package.json +7 -7
  98. package/scripts/build-binary.ts +7 -0
  99. package/src/async/job-manager.ts +334 -6
  100. package/src/cli/args.ts +9 -2
  101. package/src/cli/auth-broker-cli.ts +1 -0
  102. package/src/cli/config-cli.ts +10 -2
  103. package/src/cli.ts +2 -0
  104. package/src/commands/deep-interview.ts +1 -0
  105. package/src/commands/harness.ts +862 -0
  106. package/src/commands/launch.ts +2 -2
  107. package/src/commands/state.ts +2 -1
  108. package/src/commands/team.ts +54 -39
  109. package/src/config/keybindings.ts +6 -0
  110. package/src/config/settings-schema.ts +13 -3
  111. package/src/config/settings.ts +5 -0
  112. package/src/dap/client.ts +17 -3
  113. package/src/debug/crash-diagnostics.ts +223 -0
  114. package/src/debug/runtime-gauges.ts +20 -0
  115. package/src/deep-interview/render-middleware.ts +372 -0
  116. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  117. package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
  118. package/src/defaults/gjc/skills/team/SKILL.md +47 -21
  119. package/src/defaults/gjc/skills/ultragoal/SKILL.md +106 -13
  120. package/src/eval/py/executor.ts +21 -1
  121. package/src/eval/py/kernel.ts +15 -0
  122. package/src/exec/bash-executor.ts +41 -0
  123. package/src/extensibility/custom-tools/types.ts +1 -0
  124. package/src/extensibility/extensions/types.ts +6 -0
  125. package/src/extensibility/shared-events.ts +1 -0
  126. package/src/gjc-runtime/cli-write-receipt.ts +31 -0
  127. package/src/gjc-runtime/deep-interview-runtime.ts +98 -42
  128. package/src/gjc-runtime/goal-mode-request.ts +11 -3
  129. package/src/gjc-runtime/ralplan-runtime.ts +235 -43
  130. package/src/gjc-runtime/state-graph.ts +86 -0
  131. package/src/gjc-runtime/state-migrations.ts +179 -0
  132. package/src/gjc-runtime/state-renderer.ts +345 -0
  133. package/src/gjc-runtime/state-runtime.ts +1155 -46
  134. package/src/gjc-runtime/state-schema.ts +192 -0
  135. package/src/gjc-runtime/state-validation.ts +49 -0
  136. package/src/gjc-runtime/state-writer.ts +749 -0
  137. package/src/gjc-runtime/team-runtime.ts +1255 -189
  138. package/src/gjc-runtime/ultragoal-runtime.ts +460 -43
  139. package/src/gjc-runtime/workflow-command-ref.ts +239 -0
  140. package/src/gjc-runtime/workflow-manifest.generated.json +1601 -0
  141. package/src/gjc-runtime/workflow-manifest.ts +427 -0
  142. package/src/harness-control-plane/classifier.ts +128 -0
  143. package/src/harness-control-plane/control-endpoint.ts +148 -0
  144. package/src/harness-control-plane/finalize.ts +222 -0
  145. package/src/harness-control-plane/frame-mapper.ts +286 -0
  146. package/src/harness-control-plane/operate.ts +225 -0
  147. package/src/harness-control-plane/owner.ts +600 -0
  148. package/src/harness-control-plane/preserve.ts +102 -0
  149. package/src/harness-control-plane/receipts.ts +216 -0
  150. package/src/harness-control-plane/rpc-adapter.ts +276 -0
  151. package/src/harness-control-plane/seams.ts +39 -0
  152. package/src/harness-control-plane/session-lease.ts +388 -0
  153. package/src/harness-control-plane/state-machine.ts +98 -0
  154. package/src/harness-control-plane/storage.ts +257 -0
  155. package/src/harness-control-plane/types.ts +214 -0
  156. package/src/hooks/skill-keywords.ts +4 -2
  157. package/src/hooks/skill-state.ts +197 -64
  158. package/src/internal-urls/agent-protocol.ts +68 -21
  159. package/src/internal-urls/artifact-protocol.ts +12 -17
  160. package/src/internal-urls/docs-index.generated.ts +3 -2
  161. package/src/internal-urls/registry-helpers.ts +19 -16
  162. package/src/internal-urls/types.ts +4 -0
  163. package/src/lsp/client.ts +18 -2
  164. package/src/main.ts +21 -5
  165. package/src/modes/bridge/auth.ts +41 -0
  166. package/src/modes/bridge/bridge-client-bridge.ts +47 -0
  167. package/src/modes/bridge/bridge-mode.ts +520 -0
  168. package/src/modes/bridge/bridge-ui-context.ts +200 -0
  169. package/src/modes/bridge/event-stream.ts +70 -0
  170. package/src/modes/components/assistant-message.ts +5 -1
  171. package/src/modes/components/custom-editor.ts +101 -0
  172. package/src/modes/components/hook-selector.ts +133 -20
  173. package/src/modes/components/jobs-overlay-model.ts +109 -0
  174. package/src/modes/components/jobs-overlay.ts +172 -0
  175. package/src/modes/components/status-line/presets.ts +7 -5
  176. package/src/modes/components/status-line/segments.ts +25 -0
  177. package/src/modes/components/status-line/types.ts +2 -0
  178. package/src/modes/components/status-line.ts +9 -1
  179. package/src/modes/controllers/event-controller.ts +71 -6
  180. package/src/modes/controllers/extension-ui-controller.ts +43 -1
  181. package/src/modes/controllers/input-controller.ts +105 -9
  182. package/src/modes/controllers/selector-controller.ts +31 -1
  183. package/src/modes/index.ts +1 -0
  184. package/src/modes/interactive-mode.ts +28 -0
  185. package/src/modes/jobs-observer.ts +204 -0
  186. package/src/modes/rpc/host-tools.ts +1 -186
  187. package/src/modes/rpc/host-uris.ts +1 -235
  188. package/src/modes/rpc/rpc-client.ts +25 -10
  189. package/src/modes/rpc/rpc-mode.ts +12 -381
  190. package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
  191. package/src/modes/shared/agent-wire/command-validation.ts +131 -0
  192. package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
  193. package/src/modes/shared/agent-wire/handshake.ts +117 -0
  194. package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
  195. package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
  196. package/src/modes/shared/agent-wire/protocol.ts +96 -0
  197. package/src/modes/shared/agent-wire/responses.ts +17 -0
  198. package/src/modes/shared/agent-wire/scopes.ts +89 -0
  199. package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
  200. package/src/modes/shared/agent-wire/ui-result.ts +48 -0
  201. package/src/modes/types.ts +2 -0
  202. package/src/prompts/agents/executor.md +13 -0
  203. package/src/prompts/tools/subagent.md +39 -4
  204. package/src/prompts/tools/task-summary.md +3 -9
  205. package/src/prompts/tools/task.md +5 -1
  206. package/src/sdk.ts +8 -0
  207. package/src/session/agent-session.ts +445 -71
  208. package/src/session/session-manager.ts +13 -1
  209. package/src/skill-state/active-state.ts +58 -65
  210. package/src/skill-state/deep-interview-mutation-guard.ts +114 -17
  211. package/src/skill-state/initial-phase.ts +2 -0
  212. package/src/skill-state/workflow-state-contract.ts +33 -4
  213. package/src/skill-state/workflow-state-version.ts +3 -0
  214. package/src/slash-commands/builtin-registry.ts +8 -0
  215. package/src/task/executor.ts +79 -13
  216. package/src/task/id.ts +33 -0
  217. package/src/task/index.ts +376 -74
  218. package/src/task/output-manager.ts +5 -4
  219. package/src/task/receipt.ts +297 -0
  220. package/src/task/render.ts +54 -134
  221. package/src/task/spawn-gate.ts +132 -0
  222. package/src/task/types.ts +104 -10
  223. package/src/tools/ask.ts +88 -27
  224. package/src/tools/ast-edit.ts +1 -0
  225. package/src/tools/ast-grep.ts +1 -0
  226. package/src/tools/bash.ts +1 -1
  227. package/src/tools/cron.ts +48 -0
  228. package/src/tools/find.ts +4 -1
  229. package/src/tools/index.ts +2 -0
  230. package/src/tools/path-utils.ts +3 -2
  231. package/src/tools/read.ts +1 -0
  232. package/src/tools/search.ts +1 -0
  233. package/src/tools/skill.ts +6 -1
  234. package/src/tools/subagent.ts +423 -79
@@ -52,8 +52,8 @@ export default class Index extends Command {
52
52
  description: "Allow starting in ~ without auto-switching to a temp dir",
53
53
  }),
54
54
  mode: Flags.string({
55
- description: "Output mode: text (default), json, rpc, or rpc-ui",
56
- options: ["text", "json", "rpc", "acp", "rpc-ui"],
55
+ description: "Output mode: text (default), json, rpc, acp, rpc-ui, or bridge",
56
+ options: ["text", "json", "rpc", "acp", "rpc-ui", "bridge"],
57
57
  }),
58
58
  print: Flags.boolean({
59
59
  char: "p",
@@ -9,9 +9,10 @@ export default class State extends Command {
9
9
  '$ gjc state write --input \'{"state":{"interview_id":"abc"}}\' --mode deep-interview --json',
10
10
  "$ gjc state clear --mode deep-interview",
11
11
  "$ gjc state deep-interview read --json",
12
- '$ gjc state ralplan write --input \'{"phase":"approval","active":true}\' --json',
12
+ '$ gjc state ralplan write --input \'{"phase":"planner","active":true}\' --json',
13
13
  "$ gjc state team contract",
14
14
  "$ gjc state deep-interview handoff --to ralplan --json",
15
+ "$ gjc state doctor --skill ralplan --json",
15
16
  ];
16
17
 
17
18
  async run(): Promise<void> {
@@ -1,11 +1,14 @@
1
1
  import { Args, Command, Flags } from "@gajae-code/utils/cli";
2
+ import { renderCliWriteReceipt } from "../gjc-runtime/cli-write-receipt";
3
+ import { renderTeamStatusMarkdown } from "../gjc-runtime/state-renderer";
2
4
  import {
3
5
  buildTeamHudSummary,
4
6
  executeGjcTeamApiOperation,
5
7
  type GjcTeamSnapshot,
6
8
  listGjcTeams,
7
- monitorGjcTeam,
9
+ monitorGjcTeamSnapshot,
8
10
  parseTeamLaunchArgs,
11
+ persistGjcTeamModeStateSummary,
9
12
  readGjcTeamEvents,
10
13
  readGjcTeamSnapshot,
11
14
  shutdownGjcTeam,
@@ -31,6 +34,7 @@ async function syncTeamHud(snapshot: GjcTeamSnapshot): Promise<void> {
31
34
  hud: await buildTeamHudSummary(snapshot, events.at(-1)),
32
35
  source: "gjc-team",
33
36
  });
37
+ await persistGjcTeamModeStateSummary(snapshot, process.cwd());
34
38
  } catch {
35
39
  // HUD sync is best-effort and must not change command semantics.
36
40
  }
@@ -42,27 +46,21 @@ function formatTaskCounts(counts: Record<string, number>): string {
42
46
  .join(" ");
43
47
  }
44
48
 
45
- function formatNotificationSummary(snapshot: GjcTeamSnapshot): string {
46
- const summary = snapshot.notification_summary;
47
- return `notifications: total=${summary.total} replay_eligible=${summary.replay_eligible} pending=${summary.by_state.pending} queued=${summary.by_state.queued} deferred=${summary.by_state.deferred} failed=${summary.by_state.failed}`;
48
- }
49
-
50
- function formatAwaitingIntegrationNextStep(snapshot: GjcTeamSnapshot): string[] {
51
- if (snapshot.phase !== "awaiting_integration") return [];
52
- return [
53
- "next: worker tasks are completed, but integration still needs leader attention before the team is complete",
54
- ];
49
+ function snapshotWriteReceipt(snapshot: GjcTeamSnapshot): Record<string, unknown> {
50
+ return {
51
+ ok: true,
52
+ team_name: snapshot.team_name,
53
+ phase: snapshot.phase,
54
+ state_dir: snapshot.state_dir,
55
+ tmux_session: snapshot.tmux_session,
56
+ tmux_target: snapshot.tmux_target,
57
+ worker_count: snapshot.workers.length,
58
+ task_counts: snapshot.task_counts,
59
+ };
55
60
  }
56
61
 
57
- function formatIntegrationSummary(snapshot: {
58
- integration_by_worker?: Record<string, { status?: string; conflict_files?: string[] }>;
59
- }): string[] {
60
- const entries = Object.entries(snapshot.integration_by_worker ?? {});
61
- if (entries.length === 0) return ["integration: no attempts recorded"];
62
- return entries.map(([worker, state]) => {
63
- const files = state.conflict_files?.length ? ` files=${state.conflict_files.join(",")}` : "";
64
- return `integration: ${worker} ${state.status ?? "unknown"}${files}`;
65
- });
62
+ function writeReceipt(value: Record<string, unknown>): void {
63
+ process.stdout.write(renderCliWriteReceipt(value));
66
64
  }
67
65
 
68
66
  function parseInputFlag(argv: string[]): Record<string, unknown> {
@@ -76,12 +74,13 @@ function parseInputFlag(argv: string[]): Record<string, unknown> {
76
74
  }
77
75
 
78
76
  export default class Team extends Command {
79
- static description = "Run native GJC tmux team orchestration; --dry-run writes ephemeral .gjc/state/team state only";
77
+ static description =
78
+ "Run native GJC tmux team orchestration from inside an existing tmux/GJC --tmux session; --dry-run writes ephemeral .gjc/state/team state only";
80
79
  static strict = false;
81
80
 
82
81
  static args = {
83
82
  action: Args.string({
84
- description: "start (default), status, list, shutdown, resume, or api",
83
+ description: "start (default), status, monitor, list, shutdown, resume, or api",
85
84
  required: false,
86
85
  }),
87
86
  };
@@ -96,8 +95,10 @@ export default class Team extends Command {
96
95
  };
97
96
 
98
97
  static examples = [
98
+ "gjc --tmux # start/attach the required tmux-backed leader session first",
99
99
  'gjc team 3:executor "Implement the approved plan"',
100
100
  "gjc team status <team-name> --json",
101
+ "gjc team monitor <team-name> --json",
101
102
  'gjc team api claim-task --input \'{"team_name":"demo","worker_id":"worker-1"}\' --json',
102
103
  'gjc team 2:executor --dry-run --json "Preview state only"',
103
104
  "gjc team shutdown <team-name>",
@@ -119,26 +120,36 @@ export default class Team extends Command {
119
120
  return;
120
121
  }
121
122
 
122
- if (action === "status" || action === "resume") {
123
+ if (action === "status") {
123
124
  const teamName = rest.find(arg => !arg.startsWith("--"));
124
125
  if (!teamName) throw new Error("missing_team_name");
125
- const snapshot = await monitorGjcTeam(teamName);
126
- await syncTeamHud(snapshot);
126
+ const snapshot = await readGjcTeamSnapshot(teamName);
127
127
  if (json) {
128
128
  writeJson(snapshot);
129
129
  return;
130
130
  }
131
131
  writeText([
132
- `team: ${snapshot.team_name}`,
133
- `phase: ${snapshot.phase}`,
134
- `tmux: ${snapshot.tmux_target || snapshot.tmux_session}`,
135
- `state: ${snapshot.state_dir}`,
136
- `tasks: ${snapshot.task_total} (${formatTaskCounts(snapshot.task_counts)})`,
137
- `workers: ${snapshot.workers.map(worker => `${worker.id}:${worker.status}`).join(" ")}`,
138
- formatNotificationSummary(snapshot),
139
- ...formatAwaitingIntegrationNextStep(snapshot),
140
- ...formatIntegrationSummary(snapshot),
132
+ renderTeamStatusMarkdown(snapshot).trimEnd(),
133
+ "- mode: read-only status; use `gjc team monitor <team>` or `gjc team resume <team>` for recovery/integration",
141
134
  ]);
135
+ void formatTaskCounts(snapshot.task_counts);
136
+ return;
137
+ }
138
+
139
+ if (action === "monitor" || action === "resume") {
140
+ const teamName = rest.find(arg => !arg.startsWith("--"));
141
+ if (!teamName) throw new Error("missing_team_name");
142
+ const snapshot = await monitorGjcTeamSnapshot(teamName);
143
+ await syncTeamHud(snapshot);
144
+ if (json) {
145
+ writeReceipt(snapshotWriteReceipt(snapshot));
146
+ return;
147
+ }
148
+ writeText([
149
+ renderTeamStatusMarkdown(snapshot).trimEnd(),
150
+ "- mode: mutating monitor; liveness recovery and integration may have run",
151
+ ]);
152
+ void formatTaskCounts(snapshot.task_counts);
142
153
  return;
143
154
  }
144
155
 
@@ -148,7 +159,7 @@ export default class Team extends Command {
148
159
  const snapshot = await shutdownGjcTeam(teamName);
149
160
  await syncTeamHud(snapshot);
150
161
  if (json) {
151
- writeJson(snapshot);
162
+ writeReceipt(snapshotWriteReceipt(snapshot));
152
163
  return;
153
164
  }
154
165
  writeText([`team: ${snapshot.team_name}`, `phase: ${snapshot.phase}`, `state: ${snapshot.state_dir}`]);
@@ -162,8 +173,12 @@ export default class Team extends Command {
162
173
  "Supported operations:",
163
174
  "send-message broadcast mailbox-list mailbox-mark-delivered mailbox-mark-notified notification-list notification-read notification-replay notification-mark-pane-attempt worker-startup-ack",
164
175
  "create-task read-task list-tasks update-task claim-task transition-task-status release-task-claim",
165
- "read-config read-manifest read-worker-status read-worker-heartbeat update-worker-heartbeat write-worker-inbox write-worker-identity",
166
- "append-event read-events await-event write-shutdown-request read-shutdown-ack read-monitor-snapshot write-monitor-snapshot read-task-approval write-task-approval",
176
+ "read-config read-manifest read-worker-status update-worker-status read-worker-heartbeat recover-stale-claims update-worker-heartbeat write-worker-inbox write-worker-identity",
177
+ "append-event read-events read-traces await-event write-shutdown-request read-shutdown-ack read-monitor-snapshot write-monitor-snapshot read-task-approval write-task-approval",
178
+ "Completion example:",
179
+ 'transition-task-status --input \'{"team_name":"demo","task_id":"task-1","to":"completed","claim_token":"...","completion_evidence":{"summary":"done","items":[{"kind":"command","status":"passed","summary":"focused tests passed","command":"bun test packages/coding-agent/test/gjc-runtime/team-runtime.test.ts"}]}}\' --json',
180
+ 'Review-only completion may use {"kind":"inspection","status":"verified","summary":"review passed","location":"agent://review"}.',
181
+ 'Typed lane task example: create-task --input \'{"team_name":"demo","subject":"Verify delivery","description":"Run verification","owner":"worker-1","lane":"verification","required_role":"executor","depends_on":["task-1"]}\' --json',
167
182
  ]);
168
183
  return;
169
184
  }
@@ -177,7 +192,7 @@ export default class Team extends Command {
177
192
  // API operations without a resolvable snapshot leave HUD state unchanged.
178
193
  }
179
194
  }
180
- writeJson(result);
195
+ writeReceipt(result as Record<string, unknown>);
181
196
  return;
182
197
  }
183
198
 
@@ -186,7 +201,7 @@ export default class Team extends Command {
186
201
  const snapshot = await startGjcTeam({ ...options, dryRun });
187
202
  await syncTeamHud(snapshot);
188
203
  if (json) {
189
- writeJson(snapshot);
204
+ writeReceipt(snapshotWriteReceipt(snapshot));
190
205
  return;
191
206
  }
192
207
  writeText([
@@ -38,6 +38,7 @@ interface AppKeybindings {
38
38
  "app.session.fork": true;
39
39
  "app.session.resume": true;
40
40
  "app.session.observe": true;
41
+ "app.jobs.open": true;
41
42
  "app.session.togglePath": true;
42
43
  "app.session.toggleSort": true;
43
44
  "app.session.rename": true;
@@ -149,6 +150,11 @@ export const KEYBINDINGS = {
149
150
  defaultKeys: "ctrl+s",
150
151
  description: "Observe subagent sessions",
151
152
  },
153
+
154
+ "app.jobs.open": {
155
+ defaultKeys: "alt+j",
156
+ description: "Open monitor/cron jobs overlay",
157
+ },
152
158
  "app.session.togglePath": {
153
159
  defaultKeys: "ctrl+p",
154
160
  description: "Toggle session path display",
@@ -74,6 +74,7 @@ export type StatusLineSegmentId =
74
74
  | "git"
75
75
  | "pr"
76
76
  | "subagents"
77
+ | "jobs"
77
78
  | "token_in"
78
79
  | "token_out"
79
80
  | "token_total"
@@ -147,6 +148,7 @@ interface StringDef {
147
148
  interface NumberDef {
148
149
  type: "number";
149
150
  default: number;
151
+ validate?: (value: number) => boolean;
150
152
  ui?: UiNumber;
151
153
  }
152
154
 
@@ -319,6 +321,12 @@ export const SETTINGS_SCHEMA = {
319
321
 
320
322
  cycleOrder: { type: "array", default: DEFAULT_CYCLE_ORDER },
321
323
 
324
+ "gjc.deepInterview.ambiguityThreshold": {
325
+ type: "number",
326
+ default: 0.05,
327
+ validate: (value: number) => Number.isFinite(value) && value > 0 && value <= 1,
328
+ },
329
+
322
330
  // ────────────────────────────────────────────────────────────────────────
323
331
  // Appearance
324
332
  // ────────────────────────────────────────────────────────────────────────
@@ -2345,11 +2353,12 @@ export const SETTINGS_SCHEMA = {
2345
2353
 
2346
2354
  "task.maxConcurrency": {
2347
2355
  type: "number",
2348
- default: 32,
2356
+ default: 8,
2349
2357
  ui: {
2350
2358
  tab: "tasks",
2351
2359
  label: "Max Concurrent Tasks",
2352
- description: "Concurrent limit for subagents",
2360
+ description:
2361
+ "Safer concurrent limit for subagents; higher fan-out still requires an explicit plan above 4 tasks.",
2353
2362
  options: [
2354
2363
  { value: "0", label: "Unlimited" },
2355
2364
  { value: "1", label: "1 task" },
@@ -2401,7 +2410,8 @@ export const SETTINGS_SCHEMA = {
2401
2410
  ui: {
2402
2411
  tab: "tasks",
2403
2412
  label: "Fork Context Max Tokens",
2404
- description: "Approximate token cap for fork-context seeds. 0 uses 25% of the target model context window.",
2413
+ description:
2414
+ "Approximate token cap for explicit full fork-context seeds. 0 uses 15% of the target model context window, with a 15k fallback when the window is unknown.",
2405
2415
  },
2406
2416
  },
2407
2417
 
@@ -297,6 +297,11 @@ export class Settings {
297
297
  return getDefault(path);
298
298
  }
299
299
 
300
+ /** Check whether a setting is present in loaded settings/overrides rather than coming from schema defaults. */
301
+ has(path: SettingPath): boolean {
302
+ return getByPath(this.#merged, path.split(".")) !== undefined;
303
+ }
304
+
300
305
  /**
301
306
  * Set a setting value (sync).
302
307
  * Updates global settings and queues a background save.
package/src/dap/client.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { logger, ptree } from "@gajae-code/utils";
2
+ import { formatCrashDiagnosticNotice, writeCrashReport } from "../debug/crash-diagnostics";
2
3
  import { NON_INTERACTIVE_ENV } from "../exec/non-interactive-env";
3
4
  import { ToolAbortError } from "../tools/tool-errors";
4
5
  import type {
@@ -532,15 +533,28 @@ export class DapClient {
532
533
  }
533
534
  }
534
535
 
535
- #handleProcessExit(): void {
536
+ async #handleProcessExit(): Promise<void> {
536
537
  if (this.#disposed) return;
537
538
  this.#disposed = true;
538
539
  const stderr = this.proc.peekStderr().trim();
539
540
  const exitCode = this.proc.exitCode;
541
+ const crashNotice = formatCrashDiagnosticNotice(
542
+ await writeCrashReport(
543
+ {
544
+ kind: "dap",
545
+ command: [this.adapter.resolvedCommand, ...this.adapter.args],
546
+ exitCode,
547
+ stderr,
548
+ protocol: this.adapter.connectMode ?? "stdio",
549
+ },
550
+ { cwd: this.cwd },
551
+ ),
552
+ );
553
+ const diagnosticSuffix = crashNotice ? `\n${crashNotice}` : "";
540
554
  const error = new Error(
541
555
  stderr
542
- ? `DAP adapter exited (code ${exitCode}): ${stderr}`
543
- : `DAP adapter exited unexpectedly (code ${exitCode})`,
556
+ ? `DAP adapter exited (code ${exitCode}): ${stderr}${diagnosticSuffix}`
557
+ : `DAP adapter exited unexpectedly (code ${exitCode})${diagnosticSuffix}`,
544
558
  );
545
559
  this.#rejectPendingRequests(error);
546
560
  }
@@ -0,0 +1,223 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+
5
+ const CRASH_DIAGNOSTICS_ENV = "GJC_CRASH_DIAGNOSTICS";
6
+ const CRASH_DIAGNOSTICS_DIR_ENV = "GJC_CRASH_DIAGNOSTICS_DIR";
7
+ const STDERR_PREVIEW_BYTES = 4096;
8
+ const DIRECTORY_MODE = 0o700;
9
+ const REPORT_FILE_MODE = 0o600;
10
+
11
+ export type CrashProcessKind = "bash" | "python" | "lsp" | "dap" | "mcp" | "browser" | "worker" | "native" | "unknown";
12
+
13
+ export type CrashClass =
14
+ | "clean_exit"
15
+ | "non_zero_exit"
16
+ | "signal_exit"
17
+ | "timeout"
18
+ | "cancelled"
19
+ | "spawn_error"
20
+ | "protocol_exit"
21
+ | "native_panic"
22
+ | "unknown";
23
+
24
+ export interface CrashClassificationInput {
25
+ kind: CrashProcessKind;
26
+ command?: string[];
27
+ exitCode?: number | null;
28
+ signal?: string | null;
29
+ cancelled?: boolean;
30
+ timedOut?: boolean;
31
+ spawnError?: unknown;
32
+ stderr?: string;
33
+ protocol?: string;
34
+ }
35
+
36
+ export interface CrashClassification {
37
+ kind: CrashProcessKind;
38
+ class: CrashClass;
39
+ crashed: boolean;
40
+ exitCode: number | null;
41
+ signal: string | null;
42
+ command?: string[];
43
+ protocol?: string;
44
+ reason: string;
45
+ }
46
+
47
+ export interface CrashReport extends CrashClassification {
48
+ schemaVersion: 1;
49
+ createdAt: string;
50
+ pid: number;
51
+ cwd: string;
52
+ stderrPreview?: string;
53
+ spawnError?: string;
54
+ }
55
+
56
+ export interface CrashReportWriteResult {
57
+ report: CrashReport;
58
+ path: string | null;
59
+ enabled: boolean;
60
+ }
61
+
62
+ export function crashDiagnosticsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
63
+ const value = env[CRASH_DIAGNOSTICS_ENV];
64
+ return value === "1" || value === "true" || value === "yes";
65
+ }
66
+
67
+ export function getCrashDiagnosticsDirectory(env: NodeJS.ProcessEnv = process.env): string {
68
+ return env[CRASH_DIAGNOSTICS_DIR_ENV] ?? path.join(os.tmpdir(), "gjc-crash-diagnostics");
69
+ }
70
+
71
+ export function classifyProcessCrash(input: CrashClassificationInput): CrashClassification {
72
+ const exitCode = input.exitCode ?? null;
73
+ const signal = input.signal ?? null;
74
+ const command = input.command;
75
+ const protocol = input.protocol;
76
+
77
+ if (input.timedOut) {
78
+ return {
79
+ kind: input.kind,
80
+ class: "timeout",
81
+ crashed: true,
82
+ exitCode,
83
+ signal,
84
+ command,
85
+ protocol,
86
+ reason: "process timed out",
87
+ };
88
+ }
89
+ if (input.cancelled) {
90
+ return {
91
+ kind: input.kind,
92
+ class: "cancelled",
93
+ crashed: false,
94
+ exitCode,
95
+ signal,
96
+ command,
97
+ protocol,
98
+ reason: "process was cancelled",
99
+ };
100
+ }
101
+ if (input.spawnError !== undefined) {
102
+ return {
103
+ kind: input.kind,
104
+ class: "spawn_error",
105
+ crashed: true,
106
+ exitCode,
107
+ signal,
108
+ command,
109
+ protocol,
110
+ reason: stringifyError(input.spawnError),
111
+ };
112
+ }
113
+ if (signal) {
114
+ return {
115
+ kind: input.kind,
116
+ class: "signal_exit",
117
+ crashed: true,
118
+ exitCode,
119
+ signal,
120
+ command,
121
+ protocol,
122
+ reason: `process exited after signal ${signal}`,
123
+ };
124
+ }
125
+ if (exitCode === 0) {
126
+ return {
127
+ kind: input.kind,
128
+ class: "clean_exit",
129
+ crashed: false,
130
+ exitCode,
131
+ signal,
132
+ command,
133
+ protocol,
134
+ reason: "process exited cleanly",
135
+ };
136
+ }
137
+ if (exitCode !== null) {
138
+ return {
139
+ kind: input.kind,
140
+ class: "non_zero_exit",
141
+ crashed: true,
142
+ exitCode,
143
+ signal,
144
+ command,
145
+ protocol,
146
+ reason: `process exited with code ${exitCode}`,
147
+ };
148
+ }
149
+ return {
150
+ kind: input.kind,
151
+ class: "protocol_exit",
152
+ crashed: true,
153
+ exitCode,
154
+ signal,
155
+ command,
156
+ protocol,
157
+ reason: "process exited before protocol completion",
158
+ };
159
+ }
160
+
161
+ export async function writeCrashReport(
162
+ input: CrashClassificationInput,
163
+ options: { cwd?: string; env?: NodeJS.ProcessEnv; now?: Date } = {},
164
+ ): Promise<CrashReportWriteResult> {
165
+ const classification = classifyProcessCrash(input);
166
+ const report: CrashReport = {
167
+ schemaVersion: 1,
168
+ createdAt: (options.now ?? new Date()).toISOString(),
169
+ pid: process.pid,
170
+ cwd: options.cwd ?? process.cwd(),
171
+ ...classification,
172
+ stderrPreview: input.stderr ? trimStartBytes(input.stderr, STDERR_PREVIEW_BYTES) : undefined,
173
+ spawnError: input.spawnError === undefined ? undefined : stringifyError(input.spawnError),
174
+ };
175
+ const enabled = crashDiagnosticsEnabled(options.env);
176
+
177
+ if (!classification.crashed || !enabled) {
178
+ return { report, path: null, enabled };
179
+ }
180
+
181
+ try {
182
+ const dir = getCrashDiagnosticsDirectory(options.env);
183
+ await ensurePrivateDiagnosticsDirectory(dir);
184
+ const filename = `${report.createdAt.replace(/[:.]/g, "-")}-${report.kind}-${report.class}-${process.pid}.json`;
185
+ const reportPath = path.join(dir, filename);
186
+ await writePrivateCrashReport(reportPath, `${JSON.stringify(report, null, 2)}\n`);
187
+ return { report, path: reportPath, enabled };
188
+ } catch {
189
+ return { report, path: null, enabled };
190
+ }
191
+ }
192
+
193
+ export function formatCrashDiagnosticNotice(result: CrashReportWriteResult): string | null {
194
+ if (!result.report.crashed || !result.enabled) return null;
195
+ const location = result.path ? ` report=${result.path}` : "";
196
+ return `[crash:${result.report.kind}:${result.report.class}] ${result.report.reason}${location}`;
197
+ }
198
+
199
+ async function ensurePrivateDiagnosticsDirectory(dir: string): Promise<void> {
200
+ await fs.mkdir(dir, { recursive: true, mode: DIRECTORY_MODE });
201
+ await fs.chmod(dir, DIRECTORY_MODE);
202
+ }
203
+
204
+ async function writePrivateCrashReport(reportPath: string, contents: string): Promise<void> {
205
+ const file = await fs.open(reportPath, "wx", REPORT_FILE_MODE);
206
+ try {
207
+ await file.writeFile(contents);
208
+ } finally {
209
+ await file.close();
210
+ }
211
+ await fs.chmod(reportPath, REPORT_FILE_MODE);
212
+ }
213
+
214
+ function stringifyError(error: unknown): string {
215
+ if (error instanceof Error) return error.message;
216
+ return String(error);
217
+ }
218
+
219
+ function trimStartBytes(value: string, maxBytes: number): string {
220
+ const bytes = Buffer.from(value);
221
+ if (bytes.byteLength <= maxBytes) return value;
222
+ return Buffer.from(bytes.subarray(bytes.byteLength - maxBytes)).toString("utf8");
223
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Runtime resource owner gauges (Stage 2 observability).
3
+ *
4
+ * Reports counts of long-lived runtime owners as plain data so memory/CPU leak
5
+ * work can see whether owner maps grow without bound across a session. This is
6
+ * framework-agnostic on purpose: it does not depend on the TUI metrics surface,
7
+ * so any consumer (debug report, a metrics bridge, a test) can sample it.
8
+ */
9
+ import { getShellSessionCount } from "../exec/bash-executor";
10
+
11
+ /**
12
+ * Current runtime owner counts, keyed by `<owner>.<resource>`. Extend as more
13
+ * owners (Python kernels, LSP clients, browser tabs, async jobs, streaming
14
+ * queues) expose count getters.
15
+ */
16
+ export function getRuntimeResourceCounts(): Record<string, number> {
17
+ return {
18
+ "bash.shellSessions": getShellSessionCount(),
19
+ };
20
+ }