@gajae-code/coding-agent 0.12.5 → 0.12.7

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 (91) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +4 -1
  3. package/dist/types/async/job-manager.d.ts +36 -1
  4. package/dist/types/extensibility/extensions/runner.d.ts +9 -7
  5. package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
  6. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  7. package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
  8. package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
  9. package/dist/types/gjc-runtime/team-store.d.ts +0 -4
  10. package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
  11. package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
  12. package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
  13. package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
  14. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
  15. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
  16. package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
  17. package/dist/types/modes/components/plugin-settings.d.ts +2 -0
  18. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  19. package/dist/types/sdk/broker/broker.d.ts +18 -0
  20. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
  21. package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
  22. package/dist/types/sdk/bus/chat-daemon-control.d.ts +2 -3
  23. package/dist/types/sdk/bus/index.d.ts +4 -0
  24. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -2
  25. package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
  26. package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
  27. package/dist/types/session/agent-session.d.ts +5 -4
  28. package/dist/types/session/attempt-record-store.d.ts +19 -0
  29. package/dist/types/session/session-manager.d.ts +1 -0
  30. package/dist/types/session/session-storage.d.ts +8 -0
  31. package/dist/types/task/receipt.d.ts +1 -0
  32. package/dist/types/task/types.d.ts +43 -0
  33. package/dist/types/tools/subagent.d.ts +15 -0
  34. package/dist/types/tools/todo-write.d.ts +3 -0
  35. package/package.json +8 -8
  36. package/scripts/capture-sticky-viewport-showcase.ts +131 -5
  37. package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
  38. package/scripts/verify-sticky-viewport-showcase.ts +620 -93
  39. package/src/async/job-manager.ts +430 -46
  40. package/src/config/model-profiles.ts +8 -0
  41. package/src/config/settings-schema.ts +17 -3
  42. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
  43. package/src/extensibility/extensions/runner.ts +80 -6
  44. package/src/extensibility/extensions/wrapper.ts +22 -15
  45. package/src/extensibility/runtime-skill-discovery.ts +4 -6
  46. package/src/gjc-runtime/launch-tmux.ts +309 -293
  47. package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
  48. package/src/gjc-runtime/psmux-detect.ts +52 -6
  49. package/src/gjc-runtime/team-launch.ts +50 -2
  50. package/src/gjc-runtime/team-runtime.ts +716 -251
  51. package/src/gjc-runtime/team-store.ts +25 -7
  52. package/src/gjc-runtime/team-workers.ts +161 -1
  53. package/src/gjc-runtime/tmux-common.ts +16 -4
  54. package/src/gjc-runtime/tmux-gc.ts +37 -5
  55. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
  56. package/src/gjc-runtime/tmux-provider-context.ts +451 -0
  57. package/src/gjc-runtime/tmux-sessions.ts +409 -156
  58. package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
  59. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
  60. package/src/gjc-runtime/ultragoal-runtime.ts +360 -156
  61. package/src/internal-urls/docs-index.generated.ts +4 -4
  62. package/src/internal-urls/local-protocol.ts +39 -18
  63. package/src/modes/acp/acp-agent.ts +27 -13
  64. package/src/modes/components/gjc-bundle-settings.ts +2 -0
  65. package/src/modes/components/plugin-settings.ts +5 -0
  66. package/src/modes/components/settings-selector.ts +4 -0
  67. package/src/modes/controllers/selector-controller.ts +1 -0
  68. package/src/prompts/tools/monitor.md +4 -4
  69. package/src/sdk/broker/broker.ts +93 -10
  70. package/src/sdk/broker/lifecycle-ledger.ts +227 -11
  71. package/src/sdk/broker/lifecycle.ts +691 -92
  72. package/src/sdk/bus/chat-daemon-control.ts +30 -6
  73. package/src/sdk/bus/index.ts +23 -6
  74. package/src/sdk/bus/telegram-daemon-contract.ts +2 -2
  75. package/src/sdk/bus/telegram-daemon.ts +283 -44
  76. package/src/sdk/bus/topic-registry.ts +133 -10
  77. package/src/sdk/session.ts +7 -6
  78. package/src/session/agent-session.ts +752 -339
  79. package/src/session/attempt-record-store.ts +106 -0
  80. package/src/session/internal/managed-session-scope.ts +252 -67
  81. package/src/session/internal/managed-session-storage.ts +1 -0
  82. package/src/session/session-manager.ts +7 -0
  83. package/src/session/session-storage.ts +146 -24
  84. package/src/task/index.ts +230 -19
  85. package/src/task/provider-retry-status.ts +4 -1
  86. package/src/task/receipt.ts +2 -0
  87. package/src/task/types.ts +10 -0
  88. package/src/tools/monitor.ts +7 -1
  89. package/src/tools/output-meta.ts +10 -2
  90. package/src/tools/subagent.ts +97 -75
  91. package/src/tools/todo-write.ts +47 -0
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  categorizeComputerChangePath,
3
- isSettingsSchemaPath,
4
- normalizeRepoPath,
5
- type UltragoalChangeCategory,
3
+ normalizeChangeSetPath,
6
4
  type UltragoalChangeSet,
7
5
  type UltragoalChangeSetPath,
8
6
  type UltragoalChangeStatus,
@@ -15,12 +13,19 @@ export async function spawnText(
15
13
  try {
16
14
  const proc = Bun.spawn(command, { cwd: options.cwd, stdout: "pipe", stderr: "pipe" });
17
15
  const timeout = setTimeout(() => proc.kill(), options.timeoutMs ?? 5000);
18
- const [stdout, stderr, exitCode] = await Promise.all([
19
- new Response(proc.stdout).text(),
20
- new Response(proc.stderr).text(),
16
+ const [stdoutBytes, stderrBytes, exitCode] = await Promise.all([
17
+ new Response(proc.stdout).arrayBuffer(),
18
+ new Response(proc.stderr).arrayBuffer(),
21
19
  proc.exited,
22
20
  ]);
23
21
  clearTimeout(timeout);
22
+ let stdout: string;
23
+ try {
24
+ stdout = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(stdoutBytes);
25
+ } catch {
26
+ return { ok: false, stdout: "", stderr: "command stdout was not valid UTF-8" };
27
+ }
28
+ const stderr = new TextDecoder().decode(stderrBytes);
24
29
  return { ok: exitCode === 0, stdout, stderr };
25
30
  } catch (error) {
26
31
  return { ok: false, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
@@ -55,61 +60,79 @@ export async function resolveGitBase(cwd: string, branch?: string): Promise<stri
55
60
  }
56
61
  if (best) return best.ref;
57
62
  }
58
- const mergeBase = await spawnText(["git", "merge-base", "HEAD", "origin/main"], { cwd, timeoutMs: 3000 });
59
- if (mergeBase.ok && mergeBase.stdout.trim()) return mergeBase.stdout.trim();
60
- return "HEAD~1";
63
+ throw new Error("unable to resolve an authoritative integration base");
61
64
  }
62
65
 
63
66
  export function parseGitNameStatus(output: string): UltragoalChangeSetPath[] {
64
67
  const rows: UltragoalChangeSetPath[] = [];
65
- for (const line of output.split("\n")) {
66
- const trimmed = line.trim();
67
- if (!trimmed) continue;
68
- const parts = trimmed.split(/\s+/);
69
- const statusCode = parts[0] ?? "";
68
+ const append = (statusCode: string, pathValue: string | undefined, oldPath: string | undefined): void => {
69
+ if (!pathValue) return;
70
70
  let status: UltragoalChangeStatus = "unknown";
71
71
  if (statusCode.startsWith("A")) status = "added";
72
72
  else if (statusCode.startsWith("M")) status = "modified";
73
73
  else if (statusCode.startsWith("D")) status = "deleted";
74
74
  else if (statusCode.startsWith("R")) status = "renamed";
75
75
  else if (statusCode.startsWith("C")) status = "copied";
76
- const pathValue = status === "renamed" || status === "copied" ? parts[2] : parts[1];
77
- if (!pathValue) continue;
78
- const oldPath = status === "renamed" || status === "copied" ? parts[1] : undefined;
79
76
  rows.push({
80
- path: normalizeRepoPath(pathValue),
81
- oldPath: oldPath ? normalizeRepoPath(oldPath) : undefined,
77
+ path: normalizeChangeSetPath(pathValue),
78
+ oldPath: oldPath ? normalizeChangeSetPath(oldPath) : undefined,
82
79
  status,
83
80
  category: categorizeComputerChangePath(pathValue),
84
81
  });
82
+ };
83
+ if (output.includes("\0")) {
84
+ const tokens = output.split("\0");
85
+ let index = 0;
86
+ while (index < tokens.length) {
87
+ const statusCode = tokens[index++] ?? "";
88
+ if (!statusCode) continue;
89
+ if (statusCode.startsWith("R") || statusCode.startsWith("C")) {
90
+ const oldPath = tokens[index++];
91
+ append(statusCode, tokens[index++], oldPath);
92
+ } else {
93
+ append(statusCode, tokens[index++], undefined);
94
+ }
95
+ }
96
+ return rows;
97
+ }
98
+ for (const line of output.split("\n")) {
99
+ if (!line.trim()) continue;
100
+ const [rawStatus = "", firstPath, secondPath] = line.split("\t");
101
+ const statusCode = rawStatus.trim();
102
+ append(
103
+ statusCode,
104
+ statusCode.startsWith("R") || statusCode.startsWith("C") ? secondPath : firstPath,
105
+ statusCode.startsWith("R") || statusCode.startsWith("C") ? firstPath : undefined,
106
+ );
85
107
  }
86
108
  return rows;
87
109
  }
88
110
 
89
- function categorizeCiChangedPath(value: string): UltragoalChangeCategory {
90
- // CI_DEV_CHANGED_PATHS intentionally carries path names only. Mixed registries
91
- // such as settings-schema.ts require diff-level narrowing; without the diff,
92
- // treating the whole registry as computer-control source forces the mandatory
93
- // computer red-team suite on unrelated settings changes.
94
- if (isSettingsSchemaPath(value)) return "other";
95
- return categorizeComputerChangePath(value);
111
+ export function parseGitUntrackedPaths(output: string): UltragoalChangeSetPath[] {
112
+ const paths = output.includes("\0") ? output.split("\0") : output.split(/\r?\n/);
113
+ return paths
114
+ .filter(pathValue => pathValue.length > 0)
115
+ .map(pathValue => ({
116
+ path: normalizeChangeSetPath(pathValue),
117
+ status: "added" as UltragoalChangeStatus,
118
+ category: categorizeComputerChangePath(pathValue),
119
+ }));
96
120
  }
97
121
 
98
- function ciDevChangedPathRows(): UltragoalChangeSetPath[] {
122
+ export function ciDevChangedPathRows(): UltragoalChangeSetPath[] {
99
123
  const raw = process.env.CI_DEV_CHANGED_PATHS;
100
124
  if (!raw) return [];
101
125
  return raw
102
126
  .split(/\r?\n/)
103
- .map(row => row.trim())
104
- .filter(Boolean)
127
+ .filter(row => row.length > 0)
105
128
  .map(pathValue => ({
106
- path: normalizeRepoPath(pathValue),
129
+ path: normalizeChangeSetPath(pathValue),
107
130
  status: "unknown" as UltragoalChangeStatus,
108
- category: categorizeCiChangedPath(pathValue),
131
+ category: categorizeComputerChangePath(pathValue),
109
132
  }));
110
133
  }
111
134
 
112
- function mergeChangeSetPaths(groups: UltragoalChangeSetPath[][]): UltragoalChangeSetPath[] {
135
+ export function mergeChangeSetPaths(groups: UltragoalChangeSetPath[][]): UltragoalChangeSetPath[] {
113
136
  const byKey = new Map<string, UltragoalChangeSetPath>();
114
137
  for (const row of groups.flat()) byKey.set(`${row.oldPath ?? ""}\u0000${row.path}`, row);
115
138
  return [...byKey.values()];
@@ -119,36 +142,59 @@ export async function computeCheckpointChangeSet(cwd: string): Promise<Ultragoal
119
142
  const ciChangedPaths = ciDevChangedPathRows();
120
143
  const inGit = await spawnText(["git", "rev-parse", "--is-inside-work-tree"], { cwd, timeoutMs: 3000 });
121
144
  if (!inGit.ok || inGit.stdout.trim() !== "true") {
122
- if (ciChangedPaths.length === 0) return undefined;
145
+ if (ciChangedPaths.length === 0)
146
+ return { source: "checkpoint-git", paths: [], captureIncomplete: true, trusted: true };
123
147
  return { source: "checkpoint-git", paths: ciChangedPaths, trusted: true };
124
148
  }
125
149
  const baseRef = await resolveGitBase(cwd);
126
150
  const base = baseRef;
127
151
  const mergeBase = await spawnText(["git", "merge-base", "HEAD", baseRef], { cwd, timeoutMs: 3000 });
128
- const [committed, unstaged, staged, stat, committedDiff, unstagedDiff, stagedDiff] = await Promise.all([
129
- spawnText(["git", "diff", "--name-status", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
130
- spawnText(["git", "diff", "--name-status"], { cwd, timeoutMs: 5000 }),
131
- spawnText(["git", "diff", "--cached", "--name-status"], { cwd, timeoutMs: 5000 }),
152
+ const [committed, unstaged, staged, untracked, stat, committedDiff, unstagedDiff, stagedDiff] = await Promise.all([
153
+ spawnText(["git", "diff", "--name-status", "-z", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
154
+ spawnText(["git", "diff", "--name-status", "-z"], { cwd, timeoutMs: 5000 }),
155
+ spawnText(["git", "diff", "--cached", "--name-status", "-z"], { cwd, timeoutMs: 5000 }),
156
+ spawnText(["git", "ls-files", "--others", "--exclude-standard", "-z"], { cwd, timeoutMs: 5000 }),
132
157
  spawnText(["git", "diff", "--stat", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
133
158
  spawnText(["git", "diff", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
134
159
  spawnText(["git", "diff"], { cwd, timeoutMs: 5000 }),
135
160
  spawnText(["git", "diff", "--cached"], { cwd, timeoutMs: 5000 }),
136
161
  ]);
137
- if (!committed.ok && !unstaged.ok && !staged.ok && ciChangedPaths.length === 0) return undefined;
138
- const gitPaths = mergeChangeSetPaths([
162
+ if (!committed.ok || !unstaged.ok || !staged.ok || !untracked.ok) {
163
+ const paths = mergeChangeSetPaths([
164
+ committed.ok ? parseGitNameStatus(committed.stdout) : [],
165
+ unstaged.ok ? parseGitNameStatus(unstaged.stdout) : [],
166
+ staged.ok ? parseGitNameStatus(staged.stdout) : [],
167
+ untracked.ok ? parseGitUntrackedPaths(untracked.stdout) : [],
168
+ ciChangedPaths,
169
+ ]);
170
+ return {
171
+ source: "checkpoint-git",
172
+ baseRef,
173
+ mergeBase: mergeBase.ok && mergeBase.stdout.trim() ? mergeBase.stdout.trim() : undefined,
174
+ headRef: "HEAD",
175
+ paths,
176
+ captureIncomplete: true,
177
+ trusted: true,
178
+ };
179
+ }
180
+ const paths = mergeChangeSetPaths([
139
181
  parseGitNameStatus(committed.stdout),
140
182
  parseGitNameStatus(unstaged.stdout),
141
183
  parseGitNameStatus(staged.stdout),
184
+ parseGitUntrackedPaths(untracked.stdout),
185
+ ciChangedPaths,
142
186
  ]);
143
- const paths = gitPaths.length > 0 ? gitPaths : ciChangedPaths;
144
187
  return {
145
188
  source: "checkpoint-git",
146
189
  baseRef,
147
190
  mergeBase: mergeBase.ok && mergeBase.stdout.trim() ? mergeBase.stdout.trim() : undefined,
148
191
  headRef: "HEAD",
149
192
  paths,
150
- rawDiffStat: stat.stdout,
151
- rawDiff: [committedDiff.stdout, unstagedDiff.stdout, stagedDiff.stdout].filter(Boolean).join("\n"),
193
+ rawDiffStat: stat.ok ? stat.stdout : undefined,
194
+ rawDiff:
195
+ committedDiff.ok && unstagedDiff.ok && stagedDiff.ok
196
+ ? [committedDiff.stdout, unstagedDiff.stdout, stagedDiff.stdout].filter(Boolean).join("\n")
197
+ : undefined,
152
198
  trusted: true,
153
199
  };
154
200
  }
@@ -159,8 +205,8 @@ export function parseUnifiedDiffPaths(diff: string): UltragoalChangeSetPath[] {
159
205
  if (!line.startsWith("diff --git ")) continue;
160
206
  const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
161
207
  if (!match) continue;
162
- const oldPath = normalizeRepoPath(match[1]!);
163
- const newPath = normalizeRepoPath(match[2]!);
208
+ const oldPath = normalizeChangeSetPath(match[1]!);
209
+ const newPath = normalizeChangeSetPath(match[2]!);
164
210
  paths.push({
165
211
  path: newPath,
166
212
  oldPath: oldPath === newPath ? undefined : oldPath,
@@ -338,6 +338,13 @@ export function validateReceiptFreshBase(input: {
338
338
  message: `Ultragoal ${input.goal.id} receipt ledger event is missing.`,
339
339
  goalId: input.goal.id,
340
340
  };
341
+ const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
342
+ if (!eventReceipt || hashStructuredValue(eventReceipt) !== hashStructuredValue(input.receipt))
343
+ return {
344
+ state: "active_stale_receipt",
345
+ message: `Ultragoal ${input.goal.id} receipt does not match its ledger event receipt.`,
346
+ goalId: input.goal.id,
347
+ };
341
348
  const generation = computeUltragoalPlanGeneration({
342
349
  plan: input.plan,
343
350
  ledger: input.ledger,