@gajae-code/coding-agent 0.6.4 → 0.7.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 +51 -0
- package/dist/types/async/job-manager.d.ts +3 -1
- package/dist/types/cli/daemon-cli.d.ts +25 -0
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/cli/notify-cli.d.ts +23 -0
- package/dist/types/cli/setup-cli.d.ts +20 -1
- package/dist/types/commands/daemon.d.ts +41 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/commands/notify.d.ts +41 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/config/model-profile-activation.d.ts +12 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +38 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/daemon/builtin.d.ts +20 -0
- package/dist/types/daemon/control-types.d.ts +57 -0
- package/dist/types/daemon/runtime.d.ts +25 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +38 -7
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +15 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/oauth-selector.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/notifications/config-commands.d.ts +26 -0
- package/dist/types/notifications/config.d.ts +61 -0
- package/dist/types/notifications/helpers.d.ts +55 -0
- package/dist/types/notifications/html-format.d.ts +62 -0
- package/dist/types/notifications/index.d.ts +28 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
- package/dist/types/notifications/telegram-cli.d.ts +19 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
- package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
- package/dist/types/notifications/telegram-daemon.d.ts +276 -0
- package/dist/types/notifications/telegram-reference.d.ts +111 -0
- package/dist/types/notifications/threaded-inbound.d.ts +58 -0
- package/dist/types/notifications/threaded-render.d.ts +66 -0
- package/dist/types/notifications/topic-registry.d.ts +67 -0
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/rlm/index.d.ts +12 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/session/agent-session.d.ts +39 -2
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/setup/credential-auto-import.d.ts +63 -0
- package/dist/types/setup/credential-import.d.ts +3 -0
- package/dist/types/setup/host-plugin-setup.d.ts +39 -0
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/dist/types/tools/ask-answer-registry.d.ts +13 -0
- package/dist/types/tools/index.d.ts +18 -0
- package/dist/types/tools/subagent.d.ts +3 -0
- package/package.json +7 -7
- package/scripts/build-binary.ts +3 -0
- package/src/async/job-manager.ts +5 -1
- package/src/cli/daemon-cli.ts +122 -0
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli/notify-cli.ts +274 -0
- package/src/cli/setup-cli.ts +173 -84
- package/src/cli.ts +3 -0
- package/src/commands/daemon.ts +47 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/migrate.ts +46 -0
- package/src/commands/notify.ts +61 -0
- package/src/commands/setup.ts +11 -1
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/config/model-profile-activation.ts +74 -5
- package/src/config/model-profiles.ts +7 -4
- package/src/config/model-registry.ts +6 -3
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +29 -0
- package/src/coordinator/contract.ts +3 -0
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/coordinator-mcp/server.ts +270 -1
- package/src/daemon/builtin.ts +46 -0
- package/src/daemon/control-types.ts +65 -0
- package/src/daemon/runtime.ts +51 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +51 -47
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -13
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +8 -0
- package/src/extensibility/gjc-plugins/injection.ts +23 -4
- package/src/extensibility/gjc-plugins/state.ts +16 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +51 -18
- package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
- package/src/gjc-runtime/goal-mode-request.ts +26 -11
- package/src/gjc-runtime/launch-tmux.ts +6 -1
- package/src/gjc-runtime/ralplan-runtime.ts +79 -50
- package/src/gjc-runtime/session-layout.ts +180 -0
- package/src/gjc-runtime/session-resolution.ts +217 -0
- package/src/gjc-runtime/state-graph.ts +1 -2
- package/src/gjc-runtime/state-migrations.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +247 -124
- package/src/gjc-runtime/state-schema.ts +2 -0
- package/src/gjc-runtime/state-writer.ts +289 -41
- package/src/gjc-runtime/team-runtime.ts +43 -19
- package/src/gjc-runtime/tmux-sessions.ts +7 -1
- package/src/gjc-runtime/ultragoal-guard.ts +102 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +226 -60
- package/src/gjc-runtime/workflow-command-ref.ts +1 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +27 -2
- package/src/gjc-runtime/workflow-manifest.ts +12 -3
- package/src/goals/tools/goal-tool.ts +11 -2
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +9 -6
- package/src/main.ts +30 -0
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/acp/acp-event-mapper.ts +1 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/components/hook-editor.ts +7 -2
- package/src/modes/components/oauth-selector.ts +19 -0
- package/src/modes/controllers/event-controller.ts +20 -0
- package/src/modes/controllers/selector-controller.ts +80 -17
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/event-contract.ts +1 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
- package/src/modes/shared/agent-wire/event-observation.ts +16 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/utils/ui-helpers.ts +23 -0
- package/src/notifications/config-commands.ts +50 -0
- package/src/notifications/config.ts +107 -0
- package/src/notifications/helpers.ts +135 -0
- package/src/notifications/html-format.ts +389 -0
- package/src/notifications/index.ts +663 -0
- package/src/notifications/rate-limit-pool.ts +179 -0
- package/src/notifications/telegram-cli.ts +194 -0
- package/src/notifications/telegram-daemon-cli.ts +74 -0
- package/src/notifications/telegram-daemon-control.ts +370 -0
- package/src/notifications/telegram-daemon.ts +1370 -0
- package/src/notifications/telegram-reference.ts +335 -0
- package/src/notifications/threaded-inbound.ts +80 -0
- package/src/notifications/threaded-render.ts +155 -0
- package/src/notifications/topic-registry.ts +133 -0
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +26 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/sdk.ts +16 -0
- package/src/session/agent-session.ts +128 -24
- package/src/session/auth-storage.ts +3 -0
- package/src/session/session-dump-format.ts +43 -2
- package/src/session/session-manager.ts +39 -5
- package/src/setup/credential-auto-import.ts +258 -0
- package/src/setup/credential-import.ts +17 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
- package/src/setup/hermes-setup.ts +1 -1
- package/src/setup/host-plugin-setup.ts +142 -0
- package/src/skill-state/active-state.ts +72 -108
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/slash-commands/builtin-registry.ts +4 -1
- package/src/task/agents.ts +1 -22
- package/src/task/executor.ts +5 -1
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +108 -16
- package/src/tools/computer.ts +58 -4
- package/src/tools/image-gen.ts +5 -8
- package/src/tools/index.ts +19 -0
- package/src/tools/inspect-image.ts +16 -11
- package/src/tools/subagent-render.ts +7 -0
- package/src/tools/subagent.ts +38 -7
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
|
@@ -12,7 +12,6 @@ import { getConfigDirs } from "../../config";
|
|
|
12
12
|
import { execCommand } from "../../exec/exec";
|
|
13
13
|
import * as typebox from "../typebox";
|
|
14
14
|
import { GreenCommand } from "./bundled/ci-green";
|
|
15
|
-
import { ReviewCommand } from "./bundled/review";
|
|
16
15
|
import type {
|
|
17
16
|
CustomCommand,
|
|
18
17
|
CustomCommandAPI,
|
|
@@ -155,12 +154,6 @@ function loadBundledCommands(sharedApi: CustomCommandAPI): LoadedCustomCommand[]
|
|
|
155
154
|
command: new GreenCommand(sharedApi),
|
|
156
155
|
source: "bundled",
|
|
157
156
|
});
|
|
158
|
-
bundled.push({
|
|
159
|
-
path: "bundled:review",
|
|
160
|
-
resolvedPath: "bundled:review",
|
|
161
|
-
command: new ReviewCommand(sharedApi),
|
|
162
|
-
source: "bundled",
|
|
163
|
-
});
|
|
164
157
|
|
|
165
158
|
return bundled;
|
|
166
159
|
}
|
|
@@ -6,6 +6,7 @@ import type { CredentialDisabledEvent, ImageContent, Model, ProviderResponseMeta
|
|
|
6
6
|
import type { KeyId } from "@gajae-code/tui";
|
|
7
7
|
import { logger } from "@gajae-code/utils";
|
|
8
8
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
9
|
+
import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/unattended-session";
|
|
9
10
|
import { type Theme, theme } from "../../modes/theme/theme";
|
|
10
11
|
import type { SessionManager } from "../../session/session-manager";
|
|
11
12
|
import type {
|
|
@@ -180,6 +181,7 @@ export class ExtensionRunner {
|
|
|
180
181
|
#getContextUsageFn: () => ContextUsage | undefined = () => undefined;
|
|
181
182
|
#compactFn: (instructionsOrOptions?: string | CompactOptions) => Promise<void> = async () => {};
|
|
182
183
|
#getSystemPromptFn: () => string[] = () => [];
|
|
184
|
+
#getWorkflowGateFn: () => WorkflowGateEmitter | undefined = () => undefined;
|
|
183
185
|
#newSessionHandler: NewSessionHandler = async () => ({ cancelled: false });
|
|
184
186
|
#branchHandler: BranchHandler = async () => ({ cancelled: false });
|
|
185
187
|
#navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false });
|
|
@@ -234,6 +236,7 @@ export class ExtensionRunner {
|
|
|
234
236
|
this.#hasPendingMessagesFn = contextActions.hasPendingMessages;
|
|
235
237
|
this.#shutdownHandler = contextActions.shutdown;
|
|
236
238
|
this.#getSystemPromptFn = contextActions.getSystemPrompt;
|
|
239
|
+
this.#getWorkflowGateFn = contextActions.getWorkflowGate ?? (() => undefined);
|
|
237
240
|
|
|
238
241
|
// Command context actions (optional, only for interactive mode)
|
|
239
242
|
if (commandContextActions) {
|
|
@@ -463,6 +466,7 @@ export class ExtensionRunner {
|
|
|
463
466
|
shutdown: () => this.#shutdownHandler(),
|
|
464
467
|
getSystemPrompt: () => this.#getSystemPromptFn(),
|
|
465
468
|
hasQueuedMessages: () => this.#hasPendingMessagesFn(), // deprecated alias
|
|
469
|
+
workflowGate: this.#getWorkflowGateFn(),
|
|
466
470
|
};
|
|
467
471
|
}
|
|
468
472
|
|
|
@@ -32,6 +32,7 @@ import type { PythonResult } from "../../eval/py/executor";
|
|
|
32
32
|
import type { BashResult } from "../../exec/bash-executor";
|
|
33
33
|
import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
34
34
|
import type { CustomEditor } from "../../modes/components/custom-editor";
|
|
35
|
+
import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/unattended-session";
|
|
35
36
|
import type { Theme } from "../../modes/theme/theme";
|
|
36
37
|
import type { CustomMessage } from "../../session/messages";
|
|
37
38
|
import type { ReadonlySessionManager, SessionManager } from "../../session/session-manager";
|
|
@@ -310,6 +311,11 @@ export interface ExtensionContext {
|
|
|
310
311
|
getSystemPrompt(): string[];
|
|
311
312
|
/** @deprecated Use hasPendingMessages() instead */
|
|
312
313
|
hasQueuedMessages(): boolean;
|
|
314
|
+
/**
|
|
315
|
+
* Unattended workflow-gate bridge. Present only when the session runs in
|
|
316
|
+
* unattended/RPC mode; `undefined` in interactive/TUI mode (notify-only).
|
|
317
|
+
*/
|
|
318
|
+
workflowGate?: WorkflowGateEmitter;
|
|
313
319
|
}
|
|
314
320
|
|
|
315
321
|
/**
|
|
@@ -1234,6 +1240,8 @@ export interface ExtensionContextActions {
|
|
|
1234
1240
|
getContextUsage: () => ContextUsage | undefined;
|
|
1235
1241
|
compact: (instructionsOrOptions?: string | CompactOptions) => Promise<void>;
|
|
1236
1242
|
getSystemPrompt: () => string[];
|
|
1243
|
+
/** Unattended workflow-gate bridge (present only in unattended/RPC mode). */
|
|
1244
|
+
getWorkflowGate?: () => WorkflowGateEmitter | undefined;
|
|
1237
1245
|
}
|
|
1238
1246
|
|
|
1239
1247
|
/** Actions for ExtensionCommandContext (ctx.* in command handlers). */
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import { resolveGjcSessionForRead, SessionResolutionError } from "../../gjc-runtime/session-resolution";
|
|
2
|
+
|
|
3
|
+
async function resolveBoundarySessionId(cwd: string, sessionId?: string): Promise<string | undefined> {
|
|
4
|
+
const normalizedSessionId = sessionId?.trim();
|
|
5
|
+
if (normalizedSessionId) return normalizedSessionId;
|
|
6
|
+
try {
|
|
7
|
+
return (await resolveGjcSessionForRead(cwd, { envSessionId: process.env.GJC_SESSION_ID })).gjcSessionId;
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (error instanceof SessionResolutionError && error.code === "no_session") return undefined;
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
import { readVisibleSkillActiveState } from "../../skill-state/active-state";
|
|
2
15
|
import { initialPhaseForSkill } from "../../skill-state/initial-phase";
|
|
3
16
|
import { readActiveSubskillsForParent } from "./state";
|
|
@@ -35,7 +48,8 @@ export async function resolveCurrentPhaseForParent(input: {
|
|
|
35
48
|
const explicitPhase = input.explicitPhase?.trim();
|
|
36
49
|
if (explicitPhase) return explicitPhase;
|
|
37
50
|
|
|
38
|
-
const
|
|
51
|
+
const resolvedSessionId = await resolveBoundarySessionId(input.cwd, input.sessionId);
|
|
52
|
+
const state = resolvedSessionId ? await readVisibleSkillActiveState(input.cwd, resolvedSessionId) : null;
|
|
39
53
|
const persistedPhase = state?.active_skills?.find(entry => entry.skill === input.parent)?.phase?.trim();
|
|
40
54
|
if (persistedPhase) return persistedPhase;
|
|
41
55
|
|
|
@@ -54,9 +68,10 @@ export async function buildSubskillInjection(input: {
|
|
|
54
68
|
activation?: LoadedSubskillActivation;
|
|
55
69
|
currentPhase?: string;
|
|
56
70
|
}): Promise<{ block: string; details?: LoadedSubskillActivation } | null> {
|
|
71
|
+
const resolvedSessionId = await resolveBoundarySessionId(input.cwd, input.sessionId);
|
|
57
72
|
const resolvedPhase = await resolveCurrentPhaseForParent({
|
|
58
73
|
cwd: input.cwd,
|
|
59
|
-
sessionId:
|
|
74
|
+
sessionId: resolvedSessionId,
|
|
60
75
|
parent: input.skillName,
|
|
61
76
|
explicitPhase: input.currentPhase,
|
|
62
77
|
});
|
|
@@ -67,9 +82,11 @@ export async function buildSubskillInjection(input: {
|
|
|
67
82
|
return { block: wrapSubskillBlock(directActivation, body), details: directActivation };
|
|
68
83
|
}
|
|
69
84
|
|
|
85
|
+
if (!resolvedSessionId) return null;
|
|
86
|
+
|
|
70
87
|
const [entry] = await readActiveSubskillsForParent({
|
|
71
88
|
cwd: input.cwd,
|
|
72
|
-
sessionId:
|
|
89
|
+
sessionId: resolvedSessionId,
|
|
73
90
|
parent: input.skillName,
|
|
74
91
|
phase: resolvedPhase,
|
|
75
92
|
});
|
|
@@ -96,9 +113,11 @@ export async function buildAgentSubskillInjection(input: {
|
|
|
96
113
|
}): Promise<string> {
|
|
97
114
|
if (!(GJC_SUBSKILL_PARENT_AGENTS as readonly string[]).includes(input.agentName)) return "";
|
|
98
115
|
|
|
116
|
+
const resolvedSessionId = await resolveBoundarySessionId(input.cwd, input.sessionId);
|
|
117
|
+
if (!resolvedSessionId) return "";
|
|
99
118
|
const entries = await readActiveSubskillsForParent({
|
|
100
119
|
cwd: input.cwd,
|
|
101
|
-
sessionId:
|
|
120
|
+
sessionId: resolvedSessionId,
|
|
102
121
|
parent: input.agentName,
|
|
103
122
|
phase: "prompt",
|
|
104
123
|
});
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
import { resolveGjcSessionForRead, SessionResolutionError } from "../../gjc-runtime/session-resolution";
|
|
2
|
+
|
|
3
|
+
async function resolveBoundarySessionId(cwd: string, sessionId?: string): Promise<string | undefined> {
|
|
4
|
+
const normalizedSessionId = sessionId?.trim();
|
|
5
|
+
if (normalizedSessionId) return normalizedSessionId;
|
|
6
|
+
try {
|
|
7
|
+
return (await resolveGjcSessionForRead(cwd, { envSessionId: process.env.GJC_SESSION_ID })).gjcSessionId;
|
|
8
|
+
} catch (error) {
|
|
9
|
+
if (error instanceof SessionResolutionError && error.code === "no_session") return undefined;
|
|
10
|
+
throw error;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
import type { ActiveSubskillEntry } from "../../skill-state/active-state";
|
|
2
15
|
import { readVisibleSkillActiveState } from "../../skill-state/active-state";
|
|
3
16
|
import type { LoadedSubskillActivation } from "./types";
|
|
@@ -21,7 +34,9 @@ export async function readActiveSubskillsForParent(input: {
|
|
|
21
34
|
parent: string;
|
|
22
35
|
phase: string;
|
|
23
36
|
}): Promise<ActiveSubskillEntry[]> {
|
|
24
|
-
const
|
|
37
|
+
const resolvedSessionId = await resolveBoundarySessionId(input.cwd, input.sessionId);
|
|
38
|
+
if (!resolvedSessionId) return [];
|
|
39
|
+
const state = await readVisibleSkillActiveState(input.cwd, resolvedSessionId);
|
|
25
40
|
const parent = input.parent.trim();
|
|
26
41
|
const phase = input.phase.trim();
|
|
27
42
|
if (!state || !parent || !phase) return [];
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
normalizeDeepInterviewEnvelope,
|
|
12
12
|
questionHash,
|
|
13
13
|
} from "./deep-interview-state";
|
|
14
|
-
import {
|
|
14
|
+
import { writeSessionActivityMarker } from "./session-resolution";
|
|
15
|
+
import { readExistingStateForMutation, writeGuardedWorkflowEnvelopeAtomic } from "./state-writer";
|
|
15
16
|
|
|
16
17
|
export * from "./deep-interview-state";
|
|
17
18
|
|
|
@@ -298,6 +299,12 @@ async function readEnvelope(statePath: string): Promise<DeepInterviewStateEnvelo
|
|
|
298
299
|
return ensureDeepInterviewStateShape(undefined);
|
|
299
300
|
}
|
|
300
301
|
|
|
302
|
+
function existingStateRevision(value: unknown): number | undefined {
|
|
303
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
304
|
+
const revision = (value as Record<string, unknown>).state_revision;
|
|
305
|
+
return typeof revision === "number" && Number.isFinite(revision) ? revision : 0;
|
|
306
|
+
}
|
|
307
|
+
|
|
301
308
|
function interviewIdOf(envelope: DeepInterviewStateEnvelope): string | undefined {
|
|
302
309
|
const inner = (envelope.state ?? {}) as Record<string, unknown>;
|
|
303
310
|
return typeof inner.interview_id === "string" ? inner.interview_id : undefined;
|
|
@@ -310,6 +317,7 @@ async function persistEnvelope(
|
|
|
310
317
|
sessionId: string | undefined,
|
|
311
318
|
command: string,
|
|
312
319
|
): Promise<void> {
|
|
320
|
+
if (!sessionId) throw new Error("deep-interview recorder requires a session id");
|
|
313
321
|
const now = new Date().toISOString();
|
|
314
322
|
const payload: Record<string, unknown> = { ...normalizeDeepInterviewEnvelope(envelope), updated_at: now };
|
|
315
323
|
// Guarantee RequiredOnWriteEnvelopeSchema fields for the fresh/absent fallback;
|
|
@@ -318,11 +326,22 @@ async function persistEnvelope(
|
|
|
318
326
|
payload.version ??= WORKFLOW_STATE_VERSION;
|
|
319
327
|
payload.active ??= true;
|
|
320
328
|
payload.current_phase ??= "interviewing";
|
|
321
|
-
|
|
329
|
+
const expectedRevision = existingStateRevision(envelope);
|
|
330
|
+
const writeResult = await writeGuardedWorkflowEnvelopeAtomic(statePath, payload, {
|
|
322
331
|
cwd,
|
|
332
|
+
policy: "source",
|
|
333
|
+
expectedRevision,
|
|
323
334
|
receipt: { cwd, skill: "deep-interview", owner: "gjc-runtime", command, sessionId, nowIso: now },
|
|
324
|
-
audit: { category: "state", verb: "write", owner: "gjc-runtime", skill: "deep-interview" },
|
|
335
|
+
audit: { category: "state", verb: "write", owner: "gjc-runtime", skill: "deep-interview", sessionId },
|
|
325
336
|
});
|
|
337
|
+
// Reflect the freshly written revision back onto the in-memory envelope so a
|
|
338
|
+
// follow-up HUD sync derives its `sourceRevision` from the persisted revision
|
|
339
|
+
// (not the stale pre-write value), otherwise the active-state writer treats the
|
|
340
|
+
// newer HUD as stale and skips it (e.g. dropping the ambiguity chip after scoring).
|
|
341
|
+
if (writeResult.written && typeof expectedRevision === "number") {
|
|
342
|
+
(envelope as Record<string, unknown>).state_revision = expectedRevision + 1;
|
|
343
|
+
}
|
|
344
|
+
await writeSessionActivityMarker(cwd, sessionId, { writer: "deep-interview-recorder", path: statePath });
|
|
326
345
|
}
|
|
327
346
|
|
|
328
347
|
/**
|
|
@@ -335,20 +354,17 @@ async function syncRecorderHud(
|
|
|
335
354
|
envelope: DeepInterviewStateEnvelope,
|
|
336
355
|
sessionId: string | undefined,
|
|
337
356
|
): Promise<void> {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
} catch {
|
|
350
|
-
// HUD sync is best-effort cache maintenance and must not change record semantics.
|
|
351
|
-
}
|
|
357
|
+
const phase = typeof envelope.current_phase === "string" ? envelope.current_phase : "interviewing";
|
|
358
|
+
await syncSkillActiveState({
|
|
359
|
+
cwd,
|
|
360
|
+
skill: "deep-interview",
|
|
361
|
+
active: phase !== "complete",
|
|
362
|
+
phase,
|
|
363
|
+
sessionId,
|
|
364
|
+
source: "gjc-runtime-deep-interview-recorder",
|
|
365
|
+
hud: deriveDeepInterviewHud(normalizeDeepInterviewEnvelope(envelope) as Record<string, unknown>, { phase }),
|
|
366
|
+
sourceRevision: (existingStateRevision(envelope) ?? 0) + 1,
|
|
367
|
+
});
|
|
352
368
|
}
|
|
353
369
|
|
|
354
370
|
/**
|
|
@@ -360,6 +376,19 @@ async function repairRecorderHudFromPersisted(
|
|
|
360
376
|
cwd: string,
|
|
361
377
|
statePath: string,
|
|
362
378
|
sessionId: string | undefined,
|
|
379
|
+
): Promise<void> {
|
|
380
|
+
try {
|
|
381
|
+
await syncDeepInterviewRecorderHud(cwd, statePath, sessionId);
|
|
382
|
+
} catch {
|
|
383
|
+
// HUD sync is best-effort cache maintenance and must not change record semantics.
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Refresh the best-effort HUD cache from persisted deep-interview state. */
|
|
388
|
+
export async function syncDeepInterviewRecorderHud(
|
|
389
|
+
cwd: string,
|
|
390
|
+
statePath: string,
|
|
391
|
+
sessionId: string | undefined,
|
|
363
392
|
): Promise<void> {
|
|
364
393
|
const read = await readExistingStateForMutation(statePath);
|
|
365
394
|
if (read.kind !== "valid") return;
|
|
@@ -384,7 +413,11 @@ export async function appendOrMergeDeepInterviewRound(
|
|
|
384
413
|
}
|
|
385
414
|
(envelope.state as Record<string, unknown>).rounds = result.rounds;
|
|
386
415
|
await persistEnvelope(cwd, statePath, envelope, options.sessionId, "gjc deep-interview record-answer");
|
|
387
|
-
|
|
416
|
+
try {
|
|
417
|
+
await syncRecorderHud(cwd, envelope, options.sessionId);
|
|
418
|
+
} catch {
|
|
419
|
+
// HUD sync is best-effort cache maintenance and must not change record semantics.
|
|
420
|
+
}
|
|
388
421
|
return { action: result.action, record: result.record };
|
|
389
422
|
}
|
|
390
423
|
|
|
@@ -7,6 +7,8 @@ import { deriveDeepInterviewHud } from "../skill-state/workflow-hud";
|
|
|
7
7
|
import { WORKFLOW_STATE_VERSION } from "../skill-state/workflow-state-contract";
|
|
8
8
|
import { normalizeDeepInterviewEnvelope } from "./deep-interview-state";
|
|
9
9
|
import { runNativeRalplanCommand } from "./ralplan-runtime";
|
|
10
|
+
import { modeStatePath, sessionSpecsDir } from "./session-layout";
|
|
11
|
+
import { resolveGjcSessionForWrite, writeSessionActivityMarker } from "./session-resolution";
|
|
10
12
|
import { runNativeStateCommand } from "./state-runtime";
|
|
11
13
|
import { appendJsonl, readExistingStateForMutation, writeArtifact, writeWorkflowEnvelopeAtomic } from "./state-writer";
|
|
12
14
|
|
|
@@ -76,10 +78,6 @@ function assertSafePathComponent(value: string, label: string): void {
|
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
function encodeSessionSegment(value: string): string {
|
|
80
|
-
return encodeURIComponent(value).replaceAll(".", "%2E");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
81
|
function defaultSpecSlug(now: Date = new Date()): string {
|
|
84
82
|
const yyyy = now.getUTCFullYear().toString().padStart(4, "0");
|
|
85
83
|
const mm = (now.getUTCMonth() + 1).toString().padStart(2, "0");
|
|
@@ -89,14 +87,10 @@ function defaultSpecSlug(now: Date = new Date()): string {
|
|
|
89
87
|
return `${yyyy}-${mm}-${dd}-${hh}${min}-${randomBytes(2).toString("hex")}`;
|
|
90
88
|
}
|
|
91
89
|
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function deepInterviewStatePath(cwd: string, sessionId: string | undefined): string {
|
|
99
|
-
return path.join(stateDirFor(cwd, sessionId), "deep-interview-state.json");
|
|
90
|
+
export function deepInterviewStatePath(cwd: string, sessionId?: string): string {
|
|
91
|
+
const resolvedSessionId = sessionId?.trim() || process.env.GJC_SESSION_ID?.trim();
|
|
92
|
+
if (!resolvedSessionId) throw new Error("deep-interview state path requires a session id");
|
|
93
|
+
return modeStatePath(cwd, resolvedSessionId, "deep-interview");
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
async function resolveSpecContent(rawSpec: string, cwd: string): Promise<string> {
|
|
@@ -117,7 +111,7 @@ interface ResolvedDeepInterviewArgs {
|
|
|
117
111
|
resolution: DeepInterviewResolution;
|
|
118
112
|
threshold: number;
|
|
119
113
|
thresholdSource: string;
|
|
120
|
-
sessionId
|
|
114
|
+
sessionId: string;
|
|
121
115
|
idea: string;
|
|
122
116
|
language?: DeepInterviewLanguagePreference;
|
|
123
117
|
json: boolean;
|
|
@@ -134,7 +128,7 @@ export interface ResolvedDeepInterviewSpecWriteArgs {
|
|
|
134
128
|
stage: "final";
|
|
135
129
|
slug: string;
|
|
136
130
|
spec: string;
|
|
137
|
-
sessionId
|
|
131
|
+
sessionId: string;
|
|
138
132
|
json: boolean;
|
|
139
133
|
deliberate: boolean;
|
|
140
134
|
handoff?: "ralplan";
|
|
@@ -277,8 +271,12 @@ async function resolveSpecWriteArgs(args: readonly string[], cwd: string): Promi
|
|
|
277
271
|
throw new DeepInterviewCommandError(2, "--spec is required for deep-interview --write");
|
|
278
272
|
}
|
|
279
273
|
|
|
280
|
-
const
|
|
281
|
-
|
|
274
|
+
const session = resolveGjcSessionForWrite(cwd, {
|
|
275
|
+
flagValue: flagValue(args, "--session-id"),
|
|
276
|
+
envSessionId: process.env.GJC_SESSION_ID,
|
|
277
|
+
});
|
|
278
|
+
const sessionId = session.gjcSessionId;
|
|
279
|
+
assertSafePathComponent(sessionId, "session-id");
|
|
282
280
|
|
|
283
281
|
const rawHandoff = flagValue(args, "--handoff")?.trim() || undefined;
|
|
284
282
|
if (rawHandoff && rawHandoff !== "ralplan") {
|
|
@@ -324,8 +322,12 @@ async function resolveSpecWriteArgs(args: readonly string[], cwd: string): Promi
|
|
|
324
322
|
}
|
|
325
323
|
|
|
326
324
|
async function resolveDeepInterviewArgs(args: readonly string[], cwd: string): Promise<ResolvedDeepInterviewArgs> {
|
|
327
|
-
const
|
|
328
|
-
|
|
325
|
+
const session = resolveGjcSessionForWrite(cwd, {
|
|
326
|
+
flagValue: flagValue(args, "--session-id"),
|
|
327
|
+
envSessionId: process.env.GJC_SESSION_ID,
|
|
328
|
+
});
|
|
329
|
+
const sessionId = session.gjcSessionId;
|
|
330
|
+
assertSafePathComponent(sessionId, "session-id");
|
|
329
331
|
|
|
330
332
|
const explicitResolutions = (["quick", "standard", "deep"] as const).filter(name => hasFlag(args, `--${name}`));
|
|
331
333
|
if (explicitResolutions.length > 1) {
|
|
@@ -402,19 +404,34 @@ export async function persistDeepInterviewSpec(
|
|
|
402
404
|
}
|
|
403
405
|
const existing = existingRead.kind === "valid" ? existingRead.value : {};
|
|
404
406
|
|
|
405
|
-
const specPath = path.join(cwd,
|
|
407
|
+
const specPath = path.join(sessionSpecsDir(cwd, resolved.sessionId), `deep-interview-${resolved.slug}.md`);
|
|
406
408
|
const content = resolved.spec.endsWith("\n") ? resolved.spec : `${resolved.spec}\n`;
|
|
407
409
|
await writeArtifact(specPath, content, {
|
|
408
410
|
cwd,
|
|
409
|
-
audit: {
|
|
411
|
+
audit: {
|
|
412
|
+
category: "artifact",
|
|
413
|
+
verb: "write",
|
|
414
|
+
owner: "gjc-runtime",
|
|
415
|
+
skill: "deep-interview",
|
|
416
|
+
sessionId: resolved.sessionId,
|
|
417
|
+
},
|
|
410
418
|
});
|
|
411
419
|
|
|
412
420
|
const sha256 = createHash("sha256").update(content).digest("hex");
|
|
413
421
|
const createdAt = new Date().toISOString();
|
|
414
422
|
await appendJsonl(
|
|
415
|
-
path.join(cwd,
|
|
423
|
+
path.join(sessionSpecsDir(cwd, resolved.sessionId), "deep-interview-index.jsonl"),
|
|
416
424
|
{ slug: resolved.slug, stage: resolved.stage, path: specPath, created_at: createdAt, sha256 },
|
|
417
|
-
{
|
|
425
|
+
{
|
|
426
|
+
cwd,
|
|
427
|
+
audit: {
|
|
428
|
+
category: "ledger",
|
|
429
|
+
verb: "append",
|
|
430
|
+
owner: "gjc-runtime",
|
|
431
|
+
skill: "deep-interview",
|
|
432
|
+
sessionId: resolved.sessionId,
|
|
433
|
+
},
|
|
434
|
+
},
|
|
418
435
|
);
|
|
419
436
|
|
|
420
437
|
const payload = normalizeDeepInterviewEnvelope({
|
|
@@ -446,9 +463,11 @@ export async function persistDeepInterviewSpec(
|
|
|
446
463
|
verb: "write",
|
|
447
464
|
owner: "gjc-runtime",
|
|
448
465
|
skill: "deep-interview",
|
|
466
|
+
sessionId: resolved.sessionId,
|
|
449
467
|
forced: resolved.force,
|
|
450
468
|
},
|
|
451
469
|
});
|
|
470
|
+
await writeSessionActivityMarker(cwd, resolved.sessionId, { writer: "deep-interview-runtime", path: statePath });
|
|
452
471
|
await syncDeepInterviewHud({
|
|
453
472
|
cwd,
|
|
454
473
|
sessionId: resolved.sessionId,
|
|
@@ -503,8 +522,15 @@ async function seedDeepInterviewState(cwd: string, resolved: ResolvedDeepIntervi
|
|
|
503
522
|
sessionId: resolved.sessionId,
|
|
504
523
|
nowIso: now,
|
|
505
524
|
},
|
|
506
|
-
audit: {
|
|
525
|
+
audit: {
|
|
526
|
+
category: "state",
|
|
527
|
+
verb: "write",
|
|
528
|
+
owner: "gjc-runtime",
|
|
529
|
+
skill: "deep-interview",
|
|
530
|
+
sessionId: resolved.sessionId,
|
|
531
|
+
},
|
|
507
532
|
});
|
|
533
|
+
await writeSessionActivityMarker(cwd, resolved.sessionId, { writer: "deep-interview-runtime", path: statePath });
|
|
508
534
|
await syncDeepInterviewHud({ cwd, sessionId: resolved.sessionId, payload, phase: "interviewing" });
|
|
509
535
|
return statePath;
|
|
510
536
|
}
|
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
type ModeChangeEntry,
|
|
9
9
|
type SessionEntry,
|
|
10
10
|
} from "../session/session-manager";
|
|
11
|
+
import { sessionStateDir, sessionUltragoalDir } from "./session-layout";
|
|
12
|
+
import { resolveGjcSessionForRead, resolveGjcSessionForWrite, writeSessionActivityMarker } from "./session-resolution";
|
|
11
13
|
import { removeFileAudited, writeJsonAtomic } from "./state-writer";
|
|
12
14
|
|
|
13
15
|
export const GJC_SESSION_FILE_ENV = "GJC_SESSION_FILE";
|
|
@@ -48,12 +50,12 @@ function isEnoent(error: unknown): boolean {
|
|
|
48
50
|
);
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
function requestPath(cwd: string): string {
|
|
52
|
-
return path.join(cwd,
|
|
53
|
+
function requestPath(cwd: string, sessionId: string): string {
|
|
54
|
+
return path.join(sessionStateDir(cwd, sessionId), "goal-mode-request.json");
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
function ultragoalGoalsPath(cwd: string): string {
|
|
56
|
-
return path.join(cwd,
|
|
57
|
+
function ultragoalGoalsPath(cwd: string, sessionId: string): string {
|
|
58
|
+
return path.join(sessionUltragoalDir(cwd, sessionId), "goals.json");
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
function isCreateGoalsArg(value: string): boolean {
|
|
@@ -65,8 +67,14 @@ export function isUltragoalCreateGoalsInvocation(args: readonly string[]): boole
|
|
|
65
67
|
return command !== undefined && isCreateGoalsArg(command);
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
export async function readUltragoalGjcObjective(
|
|
69
|
-
|
|
70
|
+
export async function readUltragoalGjcObjective(
|
|
71
|
+
cwd: string,
|
|
72
|
+
sessionId?: string | null,
|
|
73
|
+
): Promise<{ objective: string; goalsPath: string }> {
|
|
74
|
+
const session = sessionId?.trim()
|
|
75
|
+
? { gjcSessionId: sessionId.trim() }
|
|
76
|
+
: await resolveGjcSessionForRead(cwd, { envSessionId: process.env.GJC_SESSION_ID });
|
|
77
|
+
const goalsPath = ultragoalGoalsPath(cwd, session.gjcSessionId);
|
|
70
78
|
try {
|
|
71
79
|
const plan = (await Bun.file(goalsPath).json()) as UltragoalPlanShape;
|
|
72
80
|
const objective = typeof plan.gjcObjective === "string" ? plan.gjcObjective.trim() : "";
|
|
@@ -87,7 +95,10 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
87
95
|
}): Promise<PendingGoalModeRequest> {
|
|
88
96
|
const objective = input.objective.trim();
|
|
89
97
|
if (!objective) throw new Error("goal objective is required");
|
|
90
|
-
const
|
|
98
|
+
const resolvedSessionId =
|
|
99
|
+
input.sessionId?.trim() ||
|
|
100
|
+
resolveGjcSessionForWrite(input.cwd, { envSessionId: process.env.GJC_SESSION_ID }).gjcSessionId;
|
|
101
|
+
const sessionId = resolvedSessionId;
|
|
91
102
|
const request: PendingGoalModeRequest = {
|
|
92
103
|
version: REQUEST_VERSION,
|
|
93
104
|
kind: "goal_mode_request",
|
|
@@ -97,11 +108,12 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
97
108
|
goalsPath: input.goalsPath,
|
|
98
109
|
...(sessionId ? { sessionId } : {}),
|
|
99
110
|
};
|
|
100
|
-
const filePath = requestPath(input.cwd);
|
|
111
|
+
const filePath = requestPath(input.cwd, sessionId);
|
|
101
112
|
await writeJsonAtomic(filePath, request, {
|
|
102
113
|
cwd: input.cwd,
|
|
103
|
-
audit: { category: "state", verb: "write", owner: "gjc-runtime" },
|
|
114
|
+
audit: { category: "state", verb: "write", owner: "gjc-runtime", sessionId },
|
|
104
115
|
});
|
|
116
|
+
await writeSessionActivityMarker(input.cwd, sessionId, { writer: "goal-mode-request", path: filePath });
|
|
105
117
|
return request;
|
|
106
118
|
}
|
|
107
119
|
|
|
@@ -175,7 +187,10 @@ export async function consumePendingGoalModeRequest(
|
|
|
175
187
|
cwd: string,
|
|
176
188
|
currentSessionId?: string | null,
|
|
177
189
|
): Promise<PendingGoalModeRequest | null> {
|
|
178
|
-
const
|
|
190
|
+
const session = currentSessionId?.trim()
|
|
191
|
+
? { gjcSessionId: currentSessionId.trim() }
|
|
192
|
+
: await resolveGjcSessionForRead(cwd, { envSessionId: process.env.GJC_SESSION_ID });
|
|
193
|
+
const filePath = requestPath(cwd, session.gjcSessionId);
|
|
179
194
|
let raw: unknown;
|
|
180
195
|
try {
|
|
181
196
|
raw = await Bun.file(filePath).json();
|
|
@@ -203,7 +218,7 @@ export async function consumePendingGoalModeRequest(
|
|
|
203
218
|
}
|
|
204
219
|
await removeFileAudited(filePath, {
|
|
205
220
|
cwd,
|
|
206
|
-
audit: { category: "prune", verb: "remove", owner: "gjc-runtime" },
|
|
221
|
+
audit: { category: "prune", verb: "remove", owner: "gjc-runtime", sessionId: session.gjcSessionId },
|
|
207
222
|
}).catch(error => {
|
|
208
223
|
if (!isEnoent(error)) throw error;
|
|
209
224
|
});
|
|
@@ -2,6 +2,7 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { safeStderrWrite } from "@gajae-code/utils";
|
|
4
4
|
import type { Args } from "../cli/args";
|
|
5
|
+
import { tmuxRuntimeSessionPath } from "./session-layout";
|
|
5
6
|
import { GJC_COORDINATOR_SESSION_ID_ENV, GJC_COORDINATOR_SESSION_STATE_FILE_ENV } from "./session-state-sidecar";
|
|
6
7
|
import {
|
|
7
8
|
buildGjcTmuxProfileCommands,
|
|
@@ -360,9 +361,13 @@ export function buildDefaultTmuxLaunchPlan(context: TmuxLaunchContext): TmuxLaun
|
|
|
360
361
|
const sessionName = buildGjcTmuxSessionName(env, { branch });
|
|
361
362
|
const tmuxCommand = resolveGjcTmuxCommand(env);
|
|
362
363
|
const sessionId = env[GJC_COORDINATOR_SESSION_ID_ENV]?.trim() || sessionName;
|
|
364
|
+
// The session ROOT is keyed by the active GJC session (GJC_SESSION_ID), NOT the
|
|
365
|
+
// coordinator/tmux identity. Fall back to the coordinator id only for standalone
|
|
366
|
+
// tmux launches with no GJC session context.
|
|
367
|
+
const gjcSessionId = env.GJC_SESSION_ID?.trim() || sessionId;
|
|
363
368
|
const sessionStateFile =
|
|
364
369
|
env[GJC_COORDINATOR_SESSION_STATE_FILE_ENV]?.trim() ||
|
|
365
|
-
|
|
370
|
+
tmuxRuntimeSessionPath(cwd, gjcSessionId, buildGjcTmuxSessionSlug(sessionName));
|
|
366
371
|
const tmuxAvailable = context.tmuxAvailable ?? Bun.which(tmuxCommand) !== null;
|
|
367
372
|
if (!tmuxAvailable) return undefined;
|
|
368
373
|
const existingSessionName =
|