@gajae-code/coding-agent 0.8.1 → 0.9.0
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.
- package/CHANGELOG.md +63 -0
- package/dist/types/append-only-mode.d.ts +31 -0
- package/dist/types/capability/rule.d.ts +8 -0
- package/dist/types/cli/completion-cli.d.ts +41 -0
- package/dist/types/cli/config-cli.d.ts +1 -0
- package/dist/types/cli/fixture-report.d.ts +20 -0
- package/dist/types/cli/fixture-report.test.d.ts +1 -0
- package/dist/types/cli/nofile-limit.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +7 -0
- package/dist/types/cli/update-cli.d.ts +22 -0
- package/dist/types/cli.d.ts +124 -1
- package/dist/types/commands/completion.d.ts +29 -0
- package/dist/types/commands/config.d.ts +3 -0
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +7 -6
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +105 -1
- package/dist/types/config/settings.d.ts +8 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/export/ttsr.d.ts +14 -3
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +39 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +17 -2
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +7 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +3 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +28 -1
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +120 -13
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/hooks/native-skill-hook.d.ts +12 -0
- package/dist/types/main.d.ts +4 -1
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +9 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +5 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +13 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/notifications/config.d.ts +14 -1
- package/dist/types/notifications/daemon-paths.d.ts +1 -0
- package/dist/types/notifications/index.d.ts +6 -3
- package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
- package/dist/types/notifications/telegram-daemon.d.ts +23 -3
- package/dist/types/notifications/telegram-reference.d.ts +0 -5
- package/dist/types/notifications/threaded-render.d.ts +6 -0
- package/dist/types/runtime-api-key.d.ts +7 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +53 -1
- package/dist/types/session/agent-storage.d.ts +1 -0
- package/dist/types/session/file-mention-pruning.d.ts +8 -0
- package/dist/types/session/messages.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +17 -1
- package/dist/types/skill-state/active-state.d.ts +18 -1
- package/dist/types/system-prompt.d.ts +18 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/fork-context-budget.d.ts +7 -0
- package/dist/types/task/token-log.d.ts +30 -0
- package/dist/types/task/token-log.test.d.ts +1 -0
- package/dist/types/tools/bisect.d.ts +81 -0
- package/dist/types/tools/computer.d.ts +2 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/utils/file-mentions.d.ts +14 -0
- package/dist/types/utils/git.d.ts +32 -0
- package/dist/types/web/scrapers/youtube.d.ts +8 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
- package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
- package/package.json +7 -7
- package/src/append-only-mode.ts +58 -0
- package/src/autoresearch/prompt.md +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/capability/rule.ts +9 -0
- package/src/cli/completion-cli.ts +234 -0
- package/src/cli/config-cli.ts +61 -9
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/fixture-report.test.ts +230 -0
- package/src/cli/fixture-report.ts +171 -0
- package/src/cli/nofile-limit.ts +48 -0
- package/src/cli/notify-cli.ts +91 -7
- package/src/cli/update-cli.ts +85 -9
- package/src/cli.ts +29 -1
- package/src/commands/completion.ts +84 -0
- package/src/commands/config.ts +2 -0
- package/src/commands/contribution-prep.ts +1 -1
- package/src/commands/deep-interview.ts +2 -1
- package/src/config/file-lock.ts +73 -34
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +65 -0
- package/src/config/model-registry.ts +14 -2
- package/src/config/settings-schema.ts +68 -1
- package/src/config/settings.ts +22 -0
- package/src/coordinator-mcp/server.ts +34 -8
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +50 -18
- package/src/defaults/gjc/skills/ralplan/SKILL.md +40 -99
- package/src/defaults/gjc/skills/team/SKILL.md +17 -14
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +59 -4
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
- package/src/defaults/gjc-defaults.ts +9 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/index.ts +1 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +60 -2
- package/src/export/ttsr.ts +96 -18
- package/src/extensibility/extensions/runner.ts +183 -219
- package/src/extensibility/extensions/types.ts +9 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +156 -44
- package/src/gjc-runtime/deep-interview-ambiguity.ts +206 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +62 -8
- package/src/gjc-runtime/deep-interview-runtime.ts +210 -2
- package/src/gjc-runtime/deep-interview-state.ts +44 -2
- package/src/gjc-runtime/session-state-sidecar.ts +263 -20
- package/src/gjc-runtime/state-runtime.ts +12 -2
- package/src/gjc-runtime/state-writer.ts +15 -0
- package/src/gjc-runtime/team-runtime.ts +187 -15
- package/src/gjc-runtime/ultragoal-guard.ts +55 -66
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +315 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +1663 -159
- package/src/gjc-runtime/workflow-manifest.generated.json +113 -2
- package/src/gjc-runtime/workflow-manifest.ts +74 -1
- package/src/goals/runtime.ts +10 -2
- package/src/hindsight/backend.ts +1 -3
- package/src/hooks/native-skill-hook.ts +124 -4
- package/src/internal-urls/docs-index.generated.ts +16 -7
- package/src/main.ts +95 -43
- package/src/memories/index.ts +12 -1
- package/src/modes/components/assistant-message.ts +241 -82
- package/src/modes/components/custom-editor.ts +1 -0
- package/src/modes/components/model-selector.ts +54 -11
- package/src/modes/components/queued-message-selector.ts +92 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/components/status-line.ts +167 -54
- package/src/modes/components/tool-execution.ts +44 -19
- package/src/modes/components/welcome.ts +160 -44
- package/src/modes/controllers/command-controller.ts +40 -4
- package/src/modes/controllers/event-controller.ts +66 -47
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +180 -18
- package/src/modes/controllers/selector-controller.ts +122 -27
- package/src/modes/interactive-mode.ts +51 -16
- package/src/modes/print-mode.ts +43 -4
- package/src/modes/prompt-action-autocomplete.ts +21 -20
- package/src/modes/rpc/rpc-mode.ts +29 -3
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/modes/shared/agent-wire/command-validation.ts +4 -0
- package/src/modes/shared/agent-wire/event-contract.ts +22 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +19 -6
- package/src/notifications/config.ts +25 -5
- package/src/notifications/daemon-paths.ts +2 -0
- package/src/notifications/index.ts +117 -14
- package/src/notifications/lifecycle-commands.ts +37 -7
- package/src/notifications/lifecycle-orchestrator.ts +19 -6
- package/src/notifications/telegram-daemon-cli.ts +2 -2
- package/src/notifications/telegram-daemon-control.ts +5 -3
- package/src/notifications/telegram-daemon.ts +302 -51
- package/src/notifications/telegram-reference.ts +0 -16
- package/src/notifications/threaded-render.ts +17 -1
- package/src/prompts/agents/architect.md +19 -10
- package/src/prompts/agents/critic.md +16 -18
- package/src/prompts/agents/executor.md +14 -11
- package/src/prompts/agents/planner.md +18 -13
- package/src/prompts/ci-green-request.md +3 -8
- package/src/prompts/goals/goal-continuation.md +0 -5
- package/src/prompts/goals/goal-mode-active.md +0 -5
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/plan-mode-subagent.md +3 -3
- package/src/prompts/system/project-prompt.md +0 -13
- package/src/prompts/system/subagent-system-prompt.md +3 -2
- package/src/prompts/system/system-prompt.md +29 -19
- package/src/prompts/system/volatile-project-context.md +10 -0
- package/src/prompts/system/web-search.md +4 -7
- package/src/prompts/tools/apply-patch.md +6 -0
- package/src/prompts/tools/ast-edit.md +1 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/bisect.md +22 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/computer.md +2 -2
- package/src/prompts/tools/cron.md +0 -2
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/find.md +1 -1
- package/src/prompts/tools/github.md +6 -6
- package/src/prompts/tools/goal.md +1 -3
- package/src/prompts/tools/image-gen.md +12 -5
- package/src/prompts/tools/irc.md +5 -4
- package/src/prompts/tools/job.md +2 -2
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/monitor.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/replace.md +2 -21
- package/src/prompts/tools/rewind.md +1 -1
- package/src/prompts/tools/search-tool-bm25.md +1 -11
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/skill.md +3 -2
- package/src/prompts/tools/ssh.md +2 -1
- package/src/prompts/tools/subagent.md +2 -2
- package/src/prompts/tools/task.md +4 -5
- package/src/prompts/tools/todo-write.md +1 -1
- package/src/prompts/tools/vim.md +1 -0
- package/src/prompts/tools/web-search.md +2 -0
- package/src/prompts/tools/write.md +8 -2
- package/src/runtime-api-key.ts +16 -0
- package/src/sdk.ts +52 -28
- package/src/session/agent-session.ts +951 -132
- package/src/session/agent-storage.ts +4 -0
- package/src/session/file-mention-pruning.ts +64 -0
- package/src/session/messages.ts +4 -0
- package/src/session/session-manager.ts +103 -4
- package/src/skill-state/active-state.ts +141 -5
- package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
- package/src/slash-commands/builtin-registry.ts +218 -71
- package/src/system-prompt.ts +57 -16
- package/src/task/executor.ts +132 -24
- package/src/task/fork-context-advisory.ts +2 -7
- package/src/task/fork-context-budget.ts +9 -0
- package/src/task/index.ts +13 -6
- package/src/task/output-manager.ts +20 -8
- package/src/task/receipt.ts +26 -7
- package/src/task/token-log.test.ts +188 -0
- package/src/task/token-log.ts +179 -0
- package/src/tools/bisect.ts +367 -0
- package/src/tools/computer.ts +246 -43
- package/src/tools/index.ts +3 -0
- package/src/tools/output-meta.ts +116 -4
- package/src/tools/search.ts +8 -2
- package/src/tools/skill.ts +15 -0
- package/src/tools/telegram-send.ts +6 -0
- package/src/utils/file-mentions.ts +39 -7
- package/src/utils/git.ts +73 -0
- package/src/web/scrapers/youtube.ts +24 -12
- package/src/workflow/workflow-intent-diff.ts +29 -6
- package/src/workflow/workflow-intent-report-consumer.ts +221 -0
- package/src/workflow/workflow-intent-report-schema.ts +129 -0
- package/src/workflow/workflow-intent-report.ts +135 -0
|
@@ -204,6 +204,7 @@ export type GjcTeamNotificationDeliveryState =
|
|
|
204
204
|
| "acknowledged";
|
|
205
205
|
|
|
206
206
|
export type GjcTeamPaneAttemptResult = "sent" | "queued" | "deferred" | "failed";
|
|
207
|
+
export type GjcTeamMailboxDeliveryTransportKind = "notifications_sdk" | "pane";
|
|
207
208
|
|
|
208
209
|
export interface GjcTeamNotification {
|
|
209
210
|
id: string;
|
|
@@ -220,6 +221,36 @@ export interface GjcTeamNotification {
|
|
|
220
221
|
updated_at: string;
|
|
221
222
|
replay_count: number;
|
|
222
223
|
}
|
|
224
|
+
export interface GjcTeamMailboxDeliveryInput {
|
|
225
|
+
team_name: string;
|
|
226
|
+
state_dir: string;
|
|
227
|
+
config: GjcTeamConfig;
|
|
228
|
+
notification: GjcTeamNotification;
|
|
229
|
+
message: GjcTeamMailboxMessage;
|
|
230
|
+
cwd: string;
|
|
231
|
+
env: NodeJS.ProcessEnv;
|
|
232
|
+
}
|
|
233
|
+
export type GjcTeamMailboxDeliveryResult =
|
|
234
|
+
| { transport: "notifications_sdk"; state: GjcTeamNotificationDeliveryState; reason?: string }
|
|
235
|
+
| { transport: "pane"; state: GjcTeamPaneAttemptResult; reason?: string };
|
|
236
|
+
export interface GjcTeamMailboxDeliveryTransport {
|
|
237
|
+
deliverMailboxMessage(input: GjcTeamMailboxDeliveryInput): Promise<GjcTeamMailboxDeliveryResult | null>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
let gjcTeamMailboxDeliveryTransport: GjcTeamMailboxDeliveryTransport | undefined;
|
|
241
|
+
|
|
242
|
+
export function setGjcTeamMailboxDeliveryTransport(transport: GjcTeamMailboxDeliveryTransport | undefined): () => void {
|
|
243
|
+
const previous = gjcTeamMailboxDeliveryTransport;
|
|
244
|
+
gjcTeamMailboxDeliveryTransport = transport;
|
|
245
|
+
return () => {
|
|
246
|
+
gjcTeamMailboxDeliveryTransport = previous;
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
export function setGjcTeamMailboxDeliveryTransportForTest(
|
|
250
|
+
transport: GjcTeamMailboxDeliveryTransport | undefined,
|
|
251
|
+
): () => void {
|
|
252
|
+
return setGjcTeamMailboxDeliveryTransport(transport);
|
|
253
|
+
}
|
|
223
254
|
|
|
224
255
|
export interface GjcTeamNotificationSummary {
|
|
225
256
|
total: number;
|
|
@@ -256,6 +287,7 @@ export interface GjcTeamStartOptions {
|
|
|
256
287
|
cwd?: string;
|
|
257
288
|
env?: NodeJS.ProcessEnv;
|
|
258
289
|
dryRun?: boolean;
|
|
290
|
+
mailboxDeliveryTransport?: GjcTeamMailboxDeliveryTransport;
|
|
259
291
|
}
|
|
260
292
|
|
|
261
293
|
export interface GjcTeamApiClaimResult {
|
|
@@ -1056,6 +1088,22 @@ async function readConfig(dir: string): Promise<GjcTeamConfig> {
|
|
|
1056
1088
|
worker_cli_plan: config.worker_cli_plan ?? Array.from({ length: config.worker_count }, () => "gjc"),
|
|
1057
1089
|
};
|
|
1058
1090
|
}
|
|
1091
|
+
const WORKER_INTEGRATION_CONFIG_CACHE_TTL_MS = 100;
|
|
1092
|
+
type WorkerIntegrationConfigCacheEntry = { checkedAt: number; mtimeMs: number; config: GjcTeamConfig };
|
|
1093
|
+
const workerIntegrationConfigCache = new Map<string, WorkerIntegrationConfigCacheEntry>();
|
|
1094
|
+
|
|
1095
|
+
async function readConfigForWorkerIntegration(dir: string): Promise<GjcTeamConfig> {
|
|
1096
|
+
const configPath = path.join(dir, "config.json");
|
|
1097
|
+
const nowMs = Date.now();
|
|
1098
|
+
const stat = await fs.stat(configPath);
|
|
1099
|
+
const cached = workerIntegrationConfigCache.get(configPath);
|
|
1100
|
+
if (cached && cached.mtimeMs === stat.mtimeMs && nowMs - cached.checkedAt <= WORKER_INTEGRATION_CONFIG_CACHE_TTL_MS)
|
|
1101
|
+
return cached.config;
|
|
1102
|
+
const config = await readConfig(dir);
|
|
1103
|
+
workerIntegrationConfigCache.set(configPath, { checkedAt: nowMs, mtimeMs: stat.mtimeMs, config });
|
|
1104
|
+
|
|
1105
|
+
return config;
|
|
1106
|
+
}
|
|
1059
1107
|
async function readPhase(dir: string): Promise<GjcTeamPhase> {
|
|
1060
1108
|
const phase = await readJsonFile<{ current_phase?: GjcTeamPhase }>(path.join(dir, "phase.json"));
|
|
1061
1109
|
return phase?.current_phase ?? "running";
|
|
@@ -1704,6 +1752,23 @@ function runGitResult(cwd: string, args: string[]): GitResult {
|
|
|
1704
1752
|
stderr: result.stderr.toString().trim(),
|
|
1705
1753
|
};
|
|
1706
1754
|
}
|
|
1755
|
+
async function runGitResultAsync(cwd: string, args: string[]): Promise<GitResult> {
|
|
1756
|
+
const result = Bun.spawn(["git", ...args], { cwd, stdout: "pipe", stderr: "pipe" });
|
|
1757
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
1758
|
+
result.exited,
|
|
1759
|
+
new Response(result.stdout).text(),
|
|
1760
|
+
new Response(result.stderr).text(),
|
|
1761
|
+
]);
|
|
1762
|
+
return {
|
|
1763
|
+
ok: exitCode === 0,
|
|
1764
|
+
stdout: stdout.trim(),
|
|
1765
|
+
stderr: stderr.trim(),
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
async function tryRunGitAsync(cwd: string, args: string[]): Promise<string | null> {
|
|
1769
|
+
const result = await runGitResultAsync(cwd, args);
|
|
1770
|
+
return result.ok ? result.stdout : null;
|
|
1771
|
+
}
|
|
1707
1772
|
function runGit(cwd: string, args: string[]): string {
|
|
1708
1773
|
const result = runGitResult(cwd, args);
|
|
1709
1774
|
if (result.ok) return result.stdout;
|
|
@@ -1896,16 +1961,39 @@ function readCurrentTmuxLeaderContext(tmuxCommand: string, env: NodeJS.ProcessEn
|
|
|
1896
1961
|
}
|
|
1897
1962
|
return { sessionName, windowIndex, leaderPaneId, target: `${sessionName}:${windowIndex}` };
|
|
1898
1963
|
}
|
|
1964
|
+
type CommandPathResolver = (command: string) => string | null;
|
|
1965
|
+
|
|
1966
|
+
function isBunVirtualPath(candidate: string | undefined): boolean {
|
|
1967
|
+
const normalized = candidate?.trim().replace(/\\/g, "/");
|
|
1968
|
+
return normalized === "/$bunfs" || normalized?.startsWith("/$bunfs/") === true;
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
function resolveFallbackGjcWorkerCommand(
|
|
1972
|
+
platform: NodeJS.Platform,
|
|
1973
|
+
execPath: string,
|
|
1974
|
+
which: CommandPathResolver,
|
|
1975
|
+
): string {
|
|
1976
|
+
const pathModule = platform === "win32" ? path.win32 : path;
|
|
1977
|
+
const quote = platform === "win32" ? powershellQuote : shellQuote;
|
|
1978
|
+
const executable = execPath.trim();
|
|
1979
|
+
if (executable && !isBunVirtualPath(executable) && pathModule.isAbsolute(executable)) return quote(executable);
|
|
1980
|
+
const gjcPath = which("gjc")?.trim();
|
|
1981
|
+
if (gjcPath && !isBunVirtualPath(gjcPath)) return quote(gjcPath);
|
|
1982
|
+
return "gjc";
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1899
1985
|
export function resolveGjcWorkerCommand(
|
|
1900
1986
|
cwd = process.cwd(),
|
|
1901
1987
|
env: NodeJS.ProcessEnv = process.env,
|
|
1902
1988
|
platform: NodeJS.Platform = process.platform,
|
|
1903
1989
|
argv: string[] = process.argv,
|
|
1904
1990
|
execPath = process.execPath,
|
|
1991
|
+
which: CommandPathResolver = Bun.which,
|
|
1905
1992
|
): string {
|
|
1906
1993
|
const explicit = env.GJC_TEAM_WORKER_COMMAND?.trim();
|
|
1907
1994
|
if (explicit) return explicit;
|
|
1908
1995
|
const entrypoint = argv[1];
|
|
1996
|
+
if (isBunVirtualPath(entrypoint)) return resolveFallbackGjcWorkerCommand(platform, execPath, which);
|
|
1909
1997
|
if (!entrypoint) return "gjc";
|
|
1910
1998
|
const pathModule = platform === "win32" ? path.win32 : path;
|
|
1911
1999
|
const resolvedEntrypoint = pathModule.isAbsolute(entrypoint) ? entrypoint : pathModule.resolve(cwd, entrypoint);
|
|
@@ -2295,6 +2383,9 @@ async function appendCommitHygieneEntries(config: GjcTeamConfig, entries: GjcTea
|
|
|
2295
2383
|
function resolveHead(cwd: string): string | null {
|
|
2296
2384
|
return tryRunGit(cwd, ["rev-parse", "HEAD"]);
|
|
2297
2385
|
}
|
|
2386
|
+
async function resolveHeadAsync(cwd: string): Promise<string | null> {
|
|
2387
|
+
return tryRunGitAsync(cwd, ["rev-parse", "HEAD"]);
|
|
2388
|
+
}
|
|
2298
2389
|
function isAncestor(cwd: string, ancestor: string, descendant: string): boolean {
|
|
2299
2390
|
return runGitResult(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]).ok;
|
|
2300
2391
|
}
|
|
@@ -2314,6 +2405,14 @@ function listConflictFiles(cwd: string): string[] {
|
|
|
2314
2405
|
.map(line => line.trim())
|
|
2315
2406
|
.filter(Boolean);
|
|
2316
2407
|
}
|
|
2408
|
+
async function listConflictFilesAsync(cwd: string): Promise<string[]> {
|
|
2409
|
+
const result = await runGitResultAsync(cwd, ["diff", "--name-only", "--diff-filter=U"]);
|
|
2410
|
+
if (!result.ok || !result.stdout) return [];
|
|
2411
|
+
return result.stdout
|
|
2412
|
+
.split(/\r?\n/)
|
|
2413
|
+
.map(line => line.trim())
|
|
2414
|
+
.filter(Boolean);
|
|
2415
|
+
}
|
|
2317
2416
|
|
|
2318
2417
|
export type GjcWorkerCheckpointClassification =
|
|
2319
2418
|
| { kind: "clean"; files: string[] }
|
|
@@ -2323,17 +2422,14 @@ export type GjcWorkerCheckpointClassification =
|
|
|
2323
2422
|
| { kind: "git_error"; files: string[]; detail: string };
|
|
2324
2423
|
|
|
2325
2424
|
const UNMERGED_GIT_STATUS_CODES = new Set(["DD", "AU", "UD", "UA", "DU", "AA", "UU"]);
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
".gjc/_session-*/rlm/",
|
|
2335
|
-
".gjc/_session-*/audit/",
|
|
2336
|
-
];
|
|
2425
|
+
// Every generated/runtime artifact for a GJC session lives under
|
|
2426
|
+
// `.gjc/_session-{id}/...` (see session-layout.ts), so worker auto-checkpoints
|
|
2427
|
+
// exclude the whole session subtree instead of enumerating its subdirectories.
|
|
2428
|
+
// The enumerated form drifted: subtrees outside the list (for example the
|
|
2429
|
+
// extragoal gate receipts from docs/extragoal-skill-template.md, or the
|
|
2430
|
+
// session-root `.session-activity.json` marker) were auto-committed and merged
|
|
2431
|
+
// into the leader branch on projects that do not gitignore `.gjc/_session-*/`.
|
|
2432
|
+
const PROTECTED_WORKER_CHECKPOINT_PREFIXES = [".gjc/_session-*/"];
|
|
2337
2433
|
|
|
2338
2434
|
function parsePorcelainStatusFiles(stdout: string): string[] {
|
|
2339
2435
|
return stdout
|
|
@@ -2384,6 +2480,26 @@ export function classifyWorkerCheckpointStatus(cwd: string): GjcWorkerCheckpoint
|
|
|
2384
2480
|
return { kind: "protected_only", files: classified.protected };
|
|
2385
2481
|
return { kind: "eligible", files: classified.eligible };
|
|
2386
2482
|
}
|
|
2483
|
+
export async function classifyWorkerCheckpointStatusAsync(cwd: string): Promise<GjcWorkerCheckpointClassification> {
|
|
2484
|
+
const status = await runGitResultAsync(cwd, ["status", "--porcelain", "-uall"]);
|
|
2485
|
+
if (!status.ok) {
|
|
2486
|
+
return { kind: "git_error", files: [], detail: status.stderr || status.stdout || "git status failed" };
|
|
2487
|
+
}
|
|
2488
|
+
if (!status.stdout.trim()) return { kind: "clean", files: [] };
|
|
2489
|
+
const files = parsePorcelainStatusFiles(status.stdout);
|
|
2490
|
+
const hasUnmergedStatus = status.stdout
|
|
2491
|
+
.split(/\r?\n/)
|
|
2492
|
+
.filter(Boolean)
|
|
2493
|
+
.some(line => UNMERGED_GIT_STATUS_CODES.has(line.slice(0, 2)));
|
|
2494
|
+
const conflictFiles = await listConflictFilesAsync(cwd);
|
|
2495
|
+
if (hasUnmergedStatus || conflictFiles.length > 0) {
|
|
2496
|
+
return { kind: "conflicted", files: conflictFiles.length > 0 ? conflictFiles : files };
|
|
2497
|
+
}
|
|
2498
|
+
const classified = classifyGjcTeamCheckpointFiles(files);
|
|
2499
|
+
if (classified.eligible.length === 0 && classified.protected.length > 0)
|
|
2500
|
+
return { kind: "protected_only", files: classified.protected };
|
|
2501
|
+
return { kind: "eligible", files: classified.eligible };
|
|
2502
|
+
}
|
|
2387
2503
|
async function appendIntegrationEvent(
|
|
2388
2504
|
dir: string,
|
|
2389
2505
|
type: string,
|
|
@@ -2861,6 +2977,7 @@ async function initializeStateDirs(dir: string, workers: GjcTeamWorker[]): Promi
|
|
|
2861
2977
|
export async function startGjcTeam(options: GjcTeamStartOptions): Promise<GjcTeamSnapshot> {
|
|
2862
2978
|
const cwd = options.cwd ?? process.cwd();
|
|
2863
2979
|
const env = options.env ?? process.env;
|
|
2980
|
+
if (options.mailboxDeliveryTransport) setGjcTeamMailboxDeliveryTransport(options.mailboxDeliveryTransport);
|
|
2864
2981
|
if (!Number.isInteger(options.workerCount) || options.workerCount < 1 || options.workerCount > GJC_TEAM_MAX_WORKERS)
|
|
2865
2982
|
throw new Error(`invalid_team_worker_count:${options.workerCount}:expected_1_${GJC_TEAM_MAX_WORKERS}`);
|
|
2866
2983
|
const workerCliPlan = resolveGjcTeamWorkerCliPlan(options.workerCount, env);
|
|
@@ -3042,13 +3159,15 @@ export async function requestGjcWorkerIntegrationAttempt(
|
|
|
3042
3159
|
const worker = env.GJC_TEAM_WORKER_ID?.trim() || env.GJC_TEAM_INTERNAL_WORKER?.split("/").pop()?.trim();
|
|
3043
3160
|
if (!teamName || !worker) return { requested: false, reason: "not_worker" };
|
|
3044
3161
|
const dir = await findTeamDir(teamName, cwd, env);
|
|
3045
|
-
const config = await
|
|
3162
|
+
const config = await readConfigForWorkerIntegration(dir);
|
|
3046
3163
|
const configuredWorker = config.workers.find(candidate => candidate.id === worker);
|
|
3047
3164
|
const worktreePath = env.GJC_TEAM_WORKTREE_PATH?.trim() || configuredWorker?.worktree_path;
|
|
3048
3165
|
if (!worktreePath || !(await pathExists(worktreePath)))
|
|
3049
3166
|
return { requested: false, reason: "missing_worktree", worker, team_name: teamName };
|
|
3050
|
-
const classification =
|
|
3051
|
-
|
|
3167
|
+
const [classification, head] = await Promise.all([
|
|
3168
|
+
classifyWorkerCheckpointStatusAsync(worktreePath),
|
|
3169
|
+
resolveHeadAsync(worktreePath),
|
|
3170
|
+
]);
|
|
3052
3171
|
if (classification.kind === "git_error") {
|
|
3053
3172
|
return { requested: false, reason: "git_error", worker, team_name: teamName, head, status: classification.kind };
|
|
3054
3173
|
}
|
|
@@ -3816,12 +3935,59 @@ async function reconcileTeamNotifications(dir: string, config: GjcTeamConfig): P
|
|
|
3816
3935
|
}
|
|
3817
3936
|
return summarizeNotifications(await listNotificationRecords(dir));
|
|
3818
3937
|
}
|
|
3938
|
+
async function messageForNotification(
|
|
3939
|
+
dir: string,
|
|
3940
|
+
notification: GjcTeamNotification,
|
|
3941
|
+
): Promise<GjcTeamMailboxMessage | null> {
|
|
3942
|
+
if (notification.kind !== "mailbox_message" || notification.source.type !== "message") return null;
|
|
3943
|
+
const mailbox = await readMailbox(dir, notification.recipient);
|
|
3944
|
+
return mailbox.messages.find(message => message.message_id === notification.source.id) ?? null;
|
|
3945
|
+
}
|
|
3946
|
+
|
|
3947
|
+
async function attemptConfiguredMailboxTransport(
|
|
3948
|
+
dir: string,
|
|
3949
|
+
config: GjcTeamConfig,
|
|
3950
|
+
notification: GjcTeamNotification,
|
|
3951
|
+
cwd: string,
|
|
3952
|
+
env: NodeJS.ProcessEnv,
|
|
3953
|
+
): Promise<GjcTeamNotification | null> {
|
|
3954
|
+
if (!gjcTeamMailboxDeliveryTransport) return null;
|
|
3955
|
+
const message = await messageForNotification(dir, notification);
|
|
3956
|
+
if (!message) return null;
|
|
3957
|
+
try {
|
|
3958
|
+
const result = await gjcTeamMailboxDeliveryTransport.deliverMailboxMessage({
|
|
3959
|
+
team_name: config.team_name,
|
|
3960
|
+
state_dir: dir,
|
|
3961
|
+
config,
|
|
3962
|
+
notification,
|
|
3963
|
+
message,
|
|
3964
|
+
cwd,
|
|
3965
|
+
env,
|
|
3966
|
+
});
|
|
3967
|
+
if (!result) return null;
|
|
3968
|
+
if (result.transport === "notifications_sdk" && result.state === "failed") return null;
|
|
3969
|
+
return writeNotificationRecord(dir, {
|
|
3970
|
+
...notification,
|
|
3971
|
+
delivery_state: result.state,
|
|
3972
|
+
pane_attempt_result: result.transport === "pane" ? result.state : undefined,
|
|
3973
|
+
pane_attempt_reason: result.reason ?? result.transport,
|
|
3974
|
+
pane_attempt_at: now(),
|
|
3975
|
+
updated_at: now(),
|
|
3976
|
+
});
|
|
3977
|
+
} catch {
|
|
3978
|
+
return null;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3819
3982
|
async function attemptPaneNotification(
|
|
3820
3983
|
dir: string,
|
|
3821
3984
|
config: GjcTeamConfig,
|
|
3822
3985
|
notification: GjcTeamNotification,
|
|
3823
3986
|
env: NodeJS.ProcessEnv,
|
|
3987
|
+
cwd = process.cwd(),
|
|
3824
3988
|
): Promise<GjcTeamNotification> {
|
|
3989
|
+
const transported = await attemptConfiguredMailboxTransport(dir, config, notification, cwd, env);
|
|
3990
|
+
if (transported) return transported;
|
|
3825
3991
|
const paneId =
|
|
3826
3992
|
notification.recipient === "leader-fixed"
|
|
3827
3993
|
? config.leader.pane_id
|
|
@@ -3868,6 +4034,7 @@ export async function replayGjcTeamNotifications(
|
|
|
3868
4034
|
replay_count: (notification.replay_count ?? 0) + 1,
|
|
3869
4035
|
},
|
|
3870
4036
|
env,
|
|
4037
|
+
cwd,
|
|
3871
4038
|
);
|
|
3872
4039
|
next.push(attempted);
|
|
3873
4040
|
}
|
|
@@ -3896,8 +4063,13 @@ export async function sendGjcTeamMessage(
|
|
|
3896
4063
|
...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),
|
|
3897
4064
|
};
|
|
3898
4065
|
const written = await writeMailboxMessage(dir, toWorker, message);
|
|
4066
|
+
const existingNotification = await readJsonFile<GjcTeamNotification>(
|
|
4067
|
+
notificationPath(dir, messageNotificationId(config.team_name, toWorker, written.message_id)),
|
|
4068
|
+
);
|
|
3899
4069
|
const notification = await createMessageNotification(dir, config.team_name, written);
|
|
3900
|
-
|
|
4070
|
+
if (!existingNotification) {
|
|
4071
|
+
await attemptPaneNotification(dir, config, notification, env, cwd);
|
|
4072
|
+
}
|
|
3901
4073
|
await appendEvent(dir, {
|
|
3902
4074
|
type: "message_sent",
|
|
3903
4075
|
worker: fromWorker,
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
3
3
|
import { resolveGjcSessionForRead, SessionResolutionError } from "./session-resolution";
|
|
4
|
+
import { validateDeferredMemberReceiptFresh, validateReceiptFreshBase } from "./ultragoal-receipt-freshness";
|
|
4
5
|
import {
|
|
5
|
-
computeUltragoalPlanGeneration,
|
|
6
6
|
getUltragoalPaths,
|
|
7
7
|
getUltragoalRunCompletionState,
|
|
8
|
-
hashStructuredValue,
|
|
9
8
|
readUltragoalLedger,
|
|
10
9
|
readUltragoalPlan,
|
|
11
10
|
recordUltragoalNudgeIfBudgetRemaining,
|
|
12
11
|
resolveUltragoalNudgeBudget,
|
|
13
12
|
selectUltragoalNudgeTarget,
|
|
14
|
-
type UltragoalCompletionVerification,
|
|
15
13
|
type UltragoalGoal,
|
|
16
14
|
type UltragoalLedgerEvent,
|
|
17
15
|
type UltragoalNudgeSurface,
|
|
@@ -176,26 +174,6 @@ function findReceiptGoal(
|
|
|
176
174
|
return storyGoal ? { goal: storyGoal, receiptKind: "per-goal" } : null;
|
|
177
175
|
}
|
|
178
176
|
|
|
179
|
-
function findLedgerReceiptEvent(
|
|
180
|
-
ledger: readonly UltragoalLedgerEvent[],
|
|
181
|
-
receipt: UltragoalCompletionVerification,
|
|
182
|
-
): UltragoalLedgerEvent | null {
|
|
183
|
-
return (
|
|
184
|
-
ledger.find(event => {
|
|
185
|
-
if (event.eventId !== receipt.checkpointLedgerEventId) return false;
|
|
186
|
-
if (event.event !== "goal_checkpointed") return false;
|
|
187
|
-
if (event.goalId !== receipt.goalId) return false;
|
|
188
|
-
const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
|
|
189
|
-
return (
|
|
190
|
-
event.status === "complete" &&
|
|
191
|
-
eventReceipt?.receiptId === receipt.receiptId &&
|
|
192
|
-
eventReceipt.receiptKind === receipt.receiptKind &&
|
|
193
|
-
eventReceipt.planGeneration === receipt.planGeneration
|
|
194
|
-
);
|
|
195
|
-
}) ?? null
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
177
|
export function validateCompletionReceipt(input: {
|
|
200
178
|
plan: UltragoalPlan;
|
|
201
179
|
ledger: readonly UltragoalLedgerEvent[];
|
|
@@ -210,55 +188,66 @@ export function validateCompletionReceipt(input: {
|
|
|
210
188
|
goalId: input.goal.id,
|
|
211
189
|
};
|
|
212
190
|
}
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
message: `Ultragoal ${input.goal.id} receipt is malformed or stale.`,
|
|
223
|
-
goalId: input.goal.id,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
const event = findLedgerReceiptEvent(input.ledger, receipt);
|
|
227
|
-
if (!event) {
|
|
228
|
-
return {
|
|
229
|
-
state: "active_stale_receipt",
|
|
230
|
-
message: `Ultragoal ${input.goal.id} receipt ledger event is missing.`,
|
|
231
|
-
goalId: input.goal.id,
|
|
232
|
-
};
|
|
191
|
+
if (receipt.validationBatch?.role === "deferred-member") {
|
|
192
|
+
return validateDeferredMemberReceiptFresh({
|
|
193
|
+
plan: input.plan,
|
|
194
|
+
ledger: input.ledger,
|
|
195
|
+
goal: input.goal,
|
|
196
|
+
receipt,
|
|
197
|
+
receiptKind: input.receiptKind,
|
|
198
|
+
requireClose: true,
|
|
199
|
+
});
|
|
233
200
|
}
|
|
234
|
-
const
|
|
201
|
+
const baseDiagnostic = validateReceiptFreshBase({
|
|
235
202
|
plan: input.plan,
|
|
236
203
|
ledger: input.ledger,
|
|
237
204
|
goal: input.goal,
|
|
205
|
+
receipt,
|
|
238
206
|
receiptKind: input.receiptKind,
|
|
239
|
-
beforeStatus: receipt.goalStatusBeforeCheckpoint,
|
|
240
|
-
excludeEventId: receipt.checkpointLedgerEventId,
|
|
241
207
|
});
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
208
|
+
if (baseDiagnostic) return baseDiagnostic;
|
|
209
|
+
if (receipt.validationBatch?.role === "batch-close") {
|
|
210
|
+
for (const memberId of receipt.validationBatch.memberIds) {
|
|
211
|
+
const member = input.plan.goals.find(goal => goal.id === memberId);
|
|
212
|
+
if (
|
|
213
|
+
!member?.validationBatch ||
|
|
214
|
+
member.validationBatch.metadataHash !== receipt.validationBatch.memberMetadataHashes[memberId]
|
|
215
|
+
) {
|
|
216
|
+
return {
|
|
217
|
+
state: "active_stale_receipt",
|
|
218
|
+
message: `Ultragoal ${input.goal.id} batch-close receipt has stale member metadata for ${memberId}.`,
|
|
219
|
+
goalId: input.goal.id,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (memberId === receipt.validationBatch.finalGoalId) continue;
|
|
223
|
+
const memberReceipt = member.completionVerification;
|
|
224
|
+
if (memberReceipt?.validationBatch?.role !== "deferred-member") {
|
|
225
|
+
return {
|
|
226
|
+
state: "active_missing_final_receipt",
|
|
227
|
+
message: `Ultragoal ${input.goal.id} batch-close receipt requires deferred member receipt for ${memberId}.`,
|
|
228
|
+
goalId: input.goal.id,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const memberDiagnostic = validateDeferredMemberReceiptFresh({
|
|
232
|
+
plan: input.plan,
|
|
233
|
+
ledger: input.ledger,
|
|
234
|
+
goal: member,
|
|
235
|
+
receipt: memberReceipt,
|
|
236
|
+
receiptKind: "per-goal",
|
|
237
|
+
requireClose: false,
|
|
238
|
+
});
|
|
239
|
+
if (memberDiagnostic.state !== "active_verified_complete") return memberDiagnostic;
|
|
240
|
+
if (
|
|
241
|
+
receipt.validationBatch.memberReceiptIds[memberId] !== memberReceipt.receiptId ||
|
|
242
|
+
receipt.validationBatch.memberChangeSetHashes[memberId] !== memberReceipt.validationBatch.changeSetHash
|
|
243
|
+
) {
|
|
244
|
+
return {
|
|
245
|
+
state: "active_stale_receipt",
|
|
246
|
+
message: `Ultragoal ${input.goal.id} batch-close receipt is stale for deferred member ${memberId}.`,
|
|
247
|
+
goalId: input.goal.id,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
262
251
|
}
|
|
263
252
|
if (input.receiptKind === "final-aggregate") {
|
|
264
253
|
const incomplete = requiredGoals(input.plan).filter(goal => goal.status !== "complete");
|