@gajae-code/coding-agent 0.2.1 → 0.2.3
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 +59 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/commands/contribution-prep.d.ts +18 -0
- package/dist/types/commands/deep-interview.d.ts +41 -0
- package/dist/types/commands/session.d.ts +24 -0
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +2 -2
- package/dist/types/config/models-config-schema.d.ts +17 -9
- package/dist/types/config/settings-schema.d.ts +37 -24
- package/dist/types/discovery/helpers.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +33 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +12 -11
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +13 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +37 -5
- package/dist/types/gjc-runtime/tmux-common.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +17 -0
- package/dist/types/goals/runtime.d.ts +3 -9
- package/dist/types/goals/state.d.ts +3 -6
- package/dist/types/goals/tools/goal-tool.d.ts +1 -69
- package/dist/types/hooks/skill-state.d.ts +5 -0
- package/dist/types/memories/index.d.ts +1 -1
- package/dist/types/memory-backend/local-backend.d.ts +3 -3
- package/dist/types/modes/components/hook-selector.d.ts +7 -0
- package/dist/types/modes/components/settings-selector.d.ts +0 -2
- package/dist/types/modes/components/status-line/types.d.ts +0 -3
- package/dist/types/modes/components/status-line.d.ts +0 -3
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -12
- package/dist/types/modes/theme/defaults/index.d.ts +0 -2
- package/dist/types/modes/theme/theme.d.ts +1 -2
- package/dist/types/modes/types.d.ts +1 -7
- package/dist/types/modes/utils/context-usage.d.ts +6 -2
- package/dist/types/sdk.d.ts +6 -2
- package/dist/types/session/agent-session.d.ts +47 -1
- package/dist/types/session/contribution-prep.d.ts +47 -0
- package/dist/types/session/session-manager.d.ts +3 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -0
- package/dist/types/setup/provider-onboarding.d.ts +29 -5
- package/dist/types/skill-state/active-state.d.ts +30 -1
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +6 -1
- package/dist/types/skill-state/initial-phase.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +9 -4
- package/dist/types/skill-state/workflow-state-contract.d.ts +34 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/types.d.ts +11 -0
- package/dist/types/tools/index.d.ts +20 -1
- package/dist/types/tools/skill.d.ts +47 -0
- package/dist/types/utils/changelog.d.ts +18 -2
- package/package.json +7 -7
- package/src/cli/args.ts +3 -2
- package/src/cli/setup-cli.ts +26 -12
- package/src/cli.ts +7 -1
- package/src/commands/contribution-prep.ts +41 -0
- package/src/commands/deep-interview.ts +30 -23
- package/src/commands/launch.ts +10 -1
- package/src/commands/ralplan.ts +10 -22
- package/src/commands/session.ts +150 -0
- package/src/commands/setup.ts +2 -0
- package/src/commands/state.ts +15 -4
- package/src/commands/team.ts +23 -3
- package/src/config/model-registry.ts +10 -2
- package/src/config/models-config-schema.ts +120 -102
- package/src/config/settings-schema.ts +42 -25
- package/src/config.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +32 -13
- package/src/defaults/gjc/skills/ralplan/SKILL.md +22 -2
- package/src/defaults/gjc/skills/team/SKILL.md +39 -7
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -25
- package/src/discovery/helpers.ts +24 -1
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +546 -0
- package/src/gjc-runtime/goal-mode-request.ts +2 -19
- package/src/gjc-runtime/launch-tmux.ts +83 -43
- package/src/gjc-runtime/ralplan-runtime.ts +460 -0
- package/src/gjc-runtime/state-runtime.ts +731 -0
- package/src/gjc-runtime/team-runtime.ts +708 -52
- package/src/gjc-runtime/tmux-common.ts +119 -0
- package/src/gjc-runtime/tmux-sessions.ts +165 -0
- package/src/gjc-runtime/ultragoal-guard.ts +6 -3
- package/src/gjc-runtime/ultragoal-runtime.ts +5 -4
- package/src/goals/runtime.ts +38 -144
- package/src/goals/state.ts +36 -7
- package/src/goals/tools/goal-tool.ts +15 -172
- package/src/hooks/skill-state.ts +39 -18
- package/src/internal-urls/docs-index.generated.ts +5 -4
- package/src/internal-urls/memory-protocol.ts +3 -2
- package/src/main.ts +2 -3
- package/src/memories/index.ts +2 -1
- package/src/memory-backend/local-backend.ts +14 -6
- package/src/modes/components/hook-selector.ts +156 -1
- package/src/modes/components/settings-selector.ts +5 -12
- package/src/modes/components/skill-hud/render.ts +4 -0
- package/src/modes/components/status-line/segments.ts +5 -16
- package/src/modes/components/status-line/types.ts +0 -3
- package/src/modes/components/status-line.ts +0 -6
- package/src/modes/controllers/command-controller.ts +27 -4
- package/src/modes/controllers/extension-ui-controller.ts +1 -0
- package/src/modes/controllers/input-controller.ts +0 -15
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/interactive-mode.ts +18 -219
- package/src/modes/theme/defaults/dark-poimandres.json +0 -1
- package/src/modes/theme/defaults/light-poimandres.json +0 -1
- package/src/modes/theme/theme.ts +0 -6
- package/src/modes/types.ts +1 -7
- package/src/modes/utils/context-usage.ts +66 -17
- package/src/prompts/agents/architect.md +3 -0
- package/src/prompts/agents/executor.md +2 -0
- package/src/prompts/agents/frontmatter.md +1 -0
- package/src/prompts/goals/goal-continuation.md +1 -4
- package/src/prompts/goals/goal-mode-active.md +3 -5
- package/src/prompts/system/subagent-system-prompt.md +6 -0
- package/src/prompts/system/system-prompt.md +5 -7
- package/src/prompts/tools/goal.md +4 -4
- package/src/prompts/tools/skill.md +28 -0
- package/src/prompts/tools/task.md +3 -0
- package/src/sdk.ts +51 -11
- package/src/session/agent-session.ts +222 -21
- package/src/session/contribution-prep.ts +320 -0
- package/src/session/session-manager.ts +9 -1
- package/src/setup/model-onboarding-guidance.ts +6 -3
- package/src/setup/provider-onboarding.ts +177 -16
- package/src/skill-state/active-state.ts +188 -25
- package/src/skill-state/deep-interview-mutation-guard.ts +72 -21
- package/src/skill-state/initial-phase.ts +17 -0
- package/src/skill-state/workflow-hud.ts +23 -5
- package/src/skill-state/workflow-state-contract.ts +121 -0
- package/src/slash-commands/builtin-registry.ts +75 -25
- package/src/slash-commands/helpers/context-report.ts +123 -13
- package/src/task/agents.ts +1 -0
- package/src/task/commands.ts +1 -5
- package/src/task/executor.ts +9 -1
- package/src/task/index.ts +91 -4
- package/src/task/types.ts +6 -0
- package/src/tools/ask.ts +2 -0
- package/src/tools/gh.ts +212 -2
- package/src/tools/index.ts +25 -6
- package/src/tools/skill.ts +153 -0
- package/src/utils/changelog.ts +67 -44
- package/dist/types/commands/gjc-runtime-bridge.d.ts +0 -30
- package/dist/types/commands/question.d.ts +0 -7
- package/dist/types/modes/loop-limit.d.ts +0 -22
- package/src/commands/gjc-runtime-bridge.ts +0 -227
- package/src/commands/question.ts +0 -12
- package/src/modes/loop-limit.ts +0 -140
- package/src/prompts/commands/orchestrate.md +0 -49
- package/src/prompts/goals/goal-budget-limit.md +0 -16
- package/src/prompts/tools/create-goal.md +0 -3
- package/src/prompts/tools/get-goal.md +0 -3
- package/src/prompts/tools/update-goal.md +0 -3
|
@@ -28,8 +28,10 @@ import {
|
|
|
28
28
|
type AgentTool,
|
|
29
29
|
AppendOnlyContextManager,
|
|
30
30
|
resolveTelemetry,
|
|
31
|
+
type StablePrefixSnapshot,
|
|
31
32
|
ThinkingLevel,
|
|
32
33
|
} from "@gajae-code/agent-core";
|
|
34
|
+
import { normalizeMessagesForProvider } from "@gajae-code/agent-core/agent-loop";
|
|
33
35
|
import {
|
|
34
36
|
AUTO_HANDOFF_THRESHOLD_FOCUS,
|
|
35
37
|
CompactionCancelledError,
|
|
@@ -75,6 +77,33 @@ import {
|
|
|
75
77
|
resolveServiceTier,
|
|
76
78
|
streamSimple,
|
|
77
79
|
} from "@gajae-code/ai";
|
|
80
|
+
|
|
81
|
+
export interface ForkContextSeedMetadata {
|
|
82
|
+
sourceSessionId: string;
|
|
83
|
+
parentMessageCount: number;
|
|
84
|
+
includedMessages: number;
|
|
85
|
+
skippedMessages: number;
|
|
86
|
+
approximateTokens: number;
|
|
87
|
+
maxMessages: number;
|
|
88
|
+
maxTokens: number;
|
|
89
|
+
skippedReasons: Record<string, number>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ForkContextSeed {
|
|
93
|
+
messages: Message[];
|
|
94
|
+
agentMessages: AgentMessage[];
|
|
95
|
+
metadata: ForkContextSeedMetadata;
|
|
96
|
+
cacheIdentity?: string;
|
|
97
|
+
appendOnlyPrefixSnapshot?: StablePrefixSnapshot;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface ForkContextSeedOptions {
|
|
101
|
+
maxMessages: number;
|
|
102
|
+
maxTokens: number;
|
|
103
|
+
cacheIdentity?: string;
|
|
104
|
+
signal?: AbortSignal;
|
|
105
|
+
}
|
|
106
|
+
|
|
78
107
|
import { MacOSPowerAssertion } from "@gajae-code/natives";
|
|
79
108
|
import {
|
|
80
109
|
extractRetryHint,
|
|
@@ -168,7 +197,7 @@ import {
|
|
|
168
197
|
} from "../runtime-mcp/discoverable-tool-metadata";
|
|
169
198
|
import { deobfuscateSessionContext, type SecretObfuscator } from "../secrets/obfuscator";
|
|
170
199
|
import { formatNoCredentialOnboardingError, formatNoModelOnboardingError } from "../setup/model-onboarding-guidance";
|
|
171
|
-
import { isCanonicalGjcWorkflowSkill
|
|
200
|
+
import { isCanonicalGjcWorkflowSkill } from "../skill-state/active-state";
|
|
172
201
|
import { assertDeepInterviewMutationAllowed } from "../skill-state/deep-interview-mutation-guard";
|
|
173
202
|
import { invalidateHostMetadata } from "../ssh/connection-manager";
|
|
174
203
|
import { resolveThinkingLevelForModel, toReasoningEffort } from "../thinking";
|
|
@@ -192,6 +221,11 @@ import { extractFileMentions, generateFileMentionMessages } from "../utils/file-
|
|
|
192
221
|
import { buildNamedToolChoice } from "../utils/tool-choice";
|
|
193
222
|
import type { AuthStorage } from "./auth-storage";
|
|
194
223
|
import type { ClientBridge, ClientBridgePermissionOption, ClientBridgePermissionOutcome } from "./client-bridge";
|
|
224
|
+
import {
|
|
225
|
+
type ContributionPrepOptions,
|
|
226
|
+
type ContributionPrepResult,
|
|
227
|
+
prepareContributionPrep,
|
|
228
|
+
} from "./contribution-prep";
|
|
195
229
|
import {
|
|
196
230
|
type BashExecutionMessage,
|
|
197
231
|
type CompactionSummaryMessage,
|
|
@@ -241,6 +275,13 @@ export type AgentSessionEvent =
|
|
|
241
275
|
| { type: "thinking_level_changed"; thinkingLevel: ThinkingLevel | undefined }
|
|
242
276
|
| { type: "goal_updated"; goal: Goal | null; state?: GoalModeState };
|
|
243
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Safe path component pattern used to validate session-id segments before
|
|
280
|
+
* joining them into `.gjc/state` paths. Mirrors the regex used by the
|
|
281
|
+
* `gjc state` runtime selector resolver.
|
|
282
|
+
*/
|
|
283
|
+
const SAFE_PATH_COMPONENT = /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,63}$/;
|
|
284
|
+
|
|
244
285
|
/** Listener function for agent session events */
|
|
245
286
|
export type AgentSessionEventListener = (event: AgentSessionEvent) => void;
|
|
246
287
|
export type AsyncJobSnapshotItem = Pick<AsyncJob, "id" | "type" | "status" | "label" | "startTime">;
|
|
@@ -278,6 +319,8 @@ export interface AgentSessionConfig {
|
|
|
278
319
|
skillsSettings?: SkillsSettings;
|
|
279
320
|
/** Model registry for API key resolution and model discovery */
|
|
280
321
|
modelRegistry: ModelRegistry;
|
|
322
|
+
/** Task recursion depth for nested sessions. Top-level sessions use 0. */
|
|
323
|
+
taskDepth?: number;
|
|
281
324
|
/** Tool registry for LSP and settings */
|
|
282
325
|
toolRegistry?: Map<string, AgentTool>;
|
|
283
326
|
/** Current session pre-LLM message transform pipeline */
|
|
@@ -326,6 +369,10 @@ export interface AgentSessionConfig {
|
|
|
326
369
|
* **MUST NOT** dispose it on their own teardown.
|
|
327
370
|
*/
|
|
328
371
|
ownedAsyncJobManager?: AsyncJobManager;
|
|
372
|
+
/** Optional fork-context seed used to initialize a child session before its first prompt. */
|
|
373
|
+
forkContextSeed?: ForkContextSeed;
|
|
374
|
+
/** Optional provider state override. Fork-context children should omit this by default. */
|
|
375
|
+
providerSessionState?: Map<string, ProviderSessionState>;
|
|
329
376
|
/** Agent identity (registry id like "0-Main" or "3-Alice") used for IRC routing. */
|
|
330
377
|
agentId?: string;
|
|
331
378
|
/** Shared agent registry (for forwarding IRC observations to the main session UI). */
|
|
@@ -337,6 +384,8 @@ export interface AgentSessionConfig {
|
|
|
337
384
|
* so that credential sticky selection is consistent with the session's streaming calls.
|
|
338
385
|
*/
|
|
339
386
|
providerSessionId?: string;
|
|
387
|
+
/** Optional provider-facing cache identity, distinct from logical session identity. */
|
|
388
|
+
providerCacheSessionId?: string;
|
|
340
389
|
}
|
|
341
390
|
|
|
342
391
|
/** Options for AgentSession.prompt() */
|
|
@@ -744,6 +793,7 @@ export class AgentSession {
|
|
|
744
793
|
readonly agent: Agent;
|
|
745
794
|
readonly sessionManager: SessionManager;
|
|
746
795
|
readonly settings: Settings;
|
|
796
|
+
readonly taskDepth: number;
|
|
747
797
|
readonly yieldQueue: YieldQueue;
|
|
748
798
|
|
|
749
799
|
#powerAssertion: MacOSPowerAssertion | undefined;
|
|
@@ -831,6 +881,7 @@ export class AgentSession {
|
|
|
831
881
|
#agentId: string | undefined;
|
|
832
882
|
#agentRegistry: AgentRegistry | undefined;
|
|
833
883
|
#providerSessionId: string | undefined;
|
|
884
|
+
#providerCacheSessionId: string | undefined;
|
|
834
885
|
#isDisposed = false;
|
|
835
886
|
// Extension system
|
|
836
887
|
#extensionRunner: ExtensionRunner | undefined = undefined;
|
|
@@ -1002,6 +1053,7 @@ export class AgentSession {
|
|
|
1002
1053
|
this.agent = config.agent;
|
|
1003
1054
|
this.sessionManager = config.sessionManager;
|
|
1004
1055
|
this.settings = config.settings;
|
|
1056
|
+
this.taskDepth = config.taskDepth ?? 0;
|
|
1005
1057
|
// Power assertions are taken per turn (see #beginInFlight); nothing acquired here.
|
|
1006
1058
|
this.#evalKernelOwnerId = config.evalKernelOwnerId ?? `agent-session:${Snowflake.next()}`;
|
|
1007
1059
|
this.#ownedAsyncJobManager = config.ownedAsyncJobManager;
|
|
@@ -1015,6 +1067,9 @@ export class AgentSession {
|
|
|
1015
1067
|
this.#customCommands = config.customCommands ?? [];
|
|
1016
1068
|
this.#skillsSettings = config.skillsSettings;
|
|
1017
1069
|
this.#modelRegistry = config.modelRegistry;
|
|
1070
|
+
if (config.providerSessionState) {
|
|
1071
|
+
this.#providerSessionState = config.providerSessionState;
|
|
1072
|
+
}
|
|
1018
1073
|
this.#validateRetryFallbackChains();
|
|
1019
1074
|
this.#toolRegistry = config.toolRegistry ?? new Map();
|
|
1020
1075
|
this.#requestedToolNames = config.requestedToolNames;
|
|
@@ -1094,6 +1149,7 @@ export class AgentSession {
|
|
|
1094
1149
|
this.#agentId = config.agentId;
|
|
1095
1150
|
this.#agentRegistry = config.agentRegistry;
|
|
1096
1151
|
this.#providerSessionId = config.providerSessionId;
|
|
1152
|
+
this.#providerCacheSessionId = config.providerCacheSessionId;
|
|
1097
1153
|
this.agent.setAssistantMessageEventInterceptor((message, assistantMessageEvent) => {
|
|
1098
1154
|
const event: AgentEvent = {
|
|
1099
1155
|
type: "message_update",
|
|
@@ -1191,6 +1247,46 @@ export class AgentSession {
|
|
|
1191
1247
|
return this.#toolChoiceQueue;
|
|
1192
1248
|
}
|
|
1193
1249
|
|
|
1250
|
+
/** Current skill prompt executing in this session, if any. */
|
|
1251
|
+
getActiveSkillState(): { skill: string; session_id?: string } | undefined {
|
|
1252
|
+
if (!this.#activeSkillState) return undefined;
|
|
1253
|
+
return {
|
|
1254
|
+
skill: this.#activeSkillState.skill,
|
|
1255
|
+
...(this.#activeSkillState.sessionId ? { session_id: this.#activeSkillState.sessionId } : {}),
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/** Best-effort accessor for the active skill's `current_phase` field from
|
|
1260
|
+
* its persisted mode-state file. Used by the `skill` tool to enforce the
|
|
1261
|
+
* terminal-phase chain guard. Returns undefined when no active skill is
|
|
1262
|
+
* recorded or the mode-state file is missing/unreadable; callers should
|
|
1263
|
+
* treat undefined as a non-terminal phase (refuses to chain). */
|
|
1264
|
+
getActiveSkillPhase(): string | undefined {
|
|
1265
|
+
const active = this.#activeSkillState;
|
|
1266
|
+
if (!active) return undefined;
|
|
1267
|
+
// Path safety: refuse to read mode-state files when the skill or
|
|
1268
|
+
// session-id are not safe path components. The `skill` tool
|
|
1269
|
+
// interprets undefined as a non-terminal phase, so chaining is
|
|
1270
|
+
// refused — there is no risk of bypassing the guard via a custom
|
|
1271
|
+
// skill name with `..` or a session-id with separators.
|
|
1272
|
+
if (!isCanonicalGjcWorkflowSkill(active.skill)) return undefined;
|
|
1273
|
+
if (active.sessionId !== undefined && !SAFE_PATH_COMPONENT.test(active.sessionId)) {
|
|
1274
|
+
return undefined;
|
|
1275
|
+
}
|
|
1276
|
+
try {
|
|
1277
|
+
const stateDir = path.join(this.sessionManager.getCwd(), ".gjc", "state");
|
|
1278
|
+
const segments = active.sessionId
|
|
1279
|
+
? [stateDir, "sessions", encodeURIComponent(active.sessionId).replaceAll(".", "%2E")]
|
|
1280
|
+
: [stateDir];
|
|
1281
|
+
const filePath = path.join(...segments, `${active.skill}-state.json`);
|
|
1282
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
1283
|
+
const parsed = JSON.parse(raw) as { current_phase?: unknown };
|
|
1284
|
+
return typeof parsed.current_phase === "string" ? parsed.current_phase : undefined;
|
|
1285
|
+
} catch {
|
|
1286
|
+
return undefined;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1194
1290
|
/** Peek the in-flight directive's invocation handler for use by the resolve tool. */
|
|
1195
1291
|
peekQueueInvoker(): ((input: unknown) => Promise<unknown> | unknown) | undefined {
|
|
1196
1292
|
return this.#toolChoiceQueue.peekInFlightInvoker();
|
|
@@ -1214,6 +1310,100 @@ export class AgentSession {
|
|
|
1214
1310
|
return this.#providerSessionState;
|
|
1215
1311
|
}
|
|
1216
1312
|
|
|
1313
|
+
async buildForkContextSeed(options: ForkContextSeedOptions): Promise<ForkContextSeed> {
|
|
1314
|
+
const transformedMessages = await this.#transformContext([...this.messages], options.signal);
|
|
1315
|
+
const convertedMessages = await this.#convertToLlm(transformedMessages);
|
|
1316
|
+
const providerMessages = this.model
|
|
1317
|
+
? normalizeMessagesForProvider(convertedMessages, this.model)
|
|
1318
|
+
: convertedMessages;
|
|
1319
|
+
const maxMessages = Math.min(500, Math.max(0, Math.trunc(options.maxMessages)));
|
|
1320
|
+
const maxTokens = Math.max(0, Math.trunc(options.maxTokens));
|
|
1321
|
+
const selected: Message[] = [];
|
|
1322
|
+
const skippedReasons: Record<string, number> = {};
|
|
1323
|
+
let skippedMessages = 0;
|
|
1324
|
+
let approximateTokens = 0;
|
|
1325
|
+
|
|
1326
|
+
const recordSkip = (reason: string) => {
|
|
1327
|
+
skippedMessages++;
|
|
1328
|
+
skippedReasons[reason] = (skippedReasons[reason] ?? 0) + 1;
|
|
1329
|
+
};
|
|
1330
|
+
|
|
1331
|
+
const sanitizeMessage = (message: Message): Message | undefined => {
|
|
1332
|
+
if (message.role === "developer") {
|
|
1333
|
+
recordSkip("developer-role");
|
|
1334
|
+
return undefined;
|
|
1335
|
+
}
|
|
1336
|
+
if (message.role === "toolResult") {
|
|
1337
|
+
recordSkip("tool-result-role");
|
|
1338
|
+
return undefined;
|
|
1339
|
+
}
|
|
1340
|
+
if (message.role !== "user" && message.role !== "assistant") {
|
|
1341
|
+
recordSkip("unsupported-role");
|
|
1342
|
+
return undefined;
|
|
1343
|
+
}
|
|
1344
|
+
const cloned = structuredClone(message) as Message;
|
|
1345
|
+
if ("providerPayload" in cloned) {
|
|
1346
|
+
delete (cloned as { providerPayload?: unknown }).providerPayload;
|
|
1347
|
+
}
|
|
1348
|
+
if (Array.isArray(cloned.content)) {
|
|
1349
|
+
const sanitizedContent: TextContent[] = [];
|
|
1350
|
+
for (const block of cloned.content) {
|
|
1351
|
+
if (block.type === "text") {
|
|
1352
|
+
sanitizedContent.push(block);
|
|
1353
|
+
} else if (block.type === "image") {
|
|
1354
|
+
sanitizedContent.push({ type: "text", text: "[Image omitted from fork-context seed]" });
|
|
1355
|
+
} else if (block.type !== "thinking") {
|
|
1356
|
+
recordSkip(`unsupported-content-${block.type}`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return { ...cloned, content: sanitizedContent } as Message;
|
|
1360
|
+
}
|
|
1361
|
+
return cloned;
|
|
1362
|
+
};
|
|
1363
|
+
|
|
1364
|
+
for (let i = providerMessages.length - 1; i >= 0; i--) {
|
|
1365
|
+
if (selected.length >= maxMessages) {
|
|
1366
|
+
recordSkip("message-limit");
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
const sanitized = sanitizeMessage(providerMessages[i]!);
|
|
1370
|
+
if (!sanitized) continue;
|
|
1371
|
+
const messageTokens = estimateTokens(sanitized);
|
|
1372
|
+
if (maxTokens > 0 && approximateTokens + messageTokens > maxTokens) {
|
|
1373
|
+
recordSkip("token-limit");
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
selected.unshift(sanitized);
|
|
1377
|
+
approximateTokens += messageTokens;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const messages = selected;
|
|
1381
|
+
let appendOnlyPrefixSnapshot: StablePrefixSnapshot | undefined;
|
|
1382
|
+
const appendOnly = this.agent.appendOnlyContext;
|
|
1383
|
+
if (appendOnly) {
|
|
1384
|
+
if (!appendOnly.prefix.built) {
|
|
1385
|
+
appendOnly.prefix.build(this.agent.state, { intentTracing: this.agent.intentTracing });
|
|
1386
|
+
}
|
|
1387
|
+
appendOnlyPrefixSnapshot = appendOnly.prefix.exportSnapshot() ?? undefined;
|
|
1388
|
+
}
|
|
1389
|
+
return {
|
|
1390
|
+
messages,
|
|
1391
|
+
agentMessages: messages.map(message => structuredClone(message) as AgentMessage),
|
|
1392
|
+
metadata: {
|
|
1393
|
+
sourceSessionId: this.sessionId,
|
|
1394
|
+
parentMessageCount: providerMessages.length,
|
|
1395
|
+
includedMessages: messages.length,
|
|
1396
|
+
skippedMessages,
|
|
1397
|
+
approximateTokens,
|
|
1398
|
+
maxMessages,
|
|
1399
|
+
maxTokens,
|
|
1400
|
+
skippedReasons,
|
|
1401
|
+
},
|
|
1402
|
+
cacheIdentity: options.cacheIdentity ?? this.sessionId,
|
|
1403
|
+
appendOnlyPrefixSnapshot,
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1217
1407
|
getHindsightSessionState(): HindsightSessionState | undefined {
|
|
1218
1408
|
return this.#hindsightSessionState;
|
|
1219
1409
|
}
|
|
@@ -1550,11 +1740,7 @@ export class AgentSession {
|
|
|
1550
1740
|
}
|
|
1551
1741
|
|
|
1552
1742
|
const targetAssistantIndex = this.#findTtsrAssistantIndex(targetMessageTimestamp);
|
|
1553
|
-
if (
|
|
1554
|
-
!this.#ttsrAbortPending ||
|
|
1555
|
-
this.#promptGeneration !== generation ||
|
|
1556
|
-
targetAssistantIndex === -1
|
|
1557
|
-
) {
|
|
1743
|
+
if (!this.#ttsrAbortPending || this.#promptGeneration !== generation) {
|
|
1558
1744
|
this.#ttsrAbortPending = false;
|
|
1559
1745
|
this.#pendingTtsrInjections = [];
|
|
1560
1746
|
this.#perToolTtsrInjections.clear();
|
|
@@ -1564,8 +1750,8 @@ export class AgentSession {
|
|
|
1564
1750
|
this.#ttsrAbortPending = false;
|
|
1565
1751
|
this.#perToolTtsrInjections.clear();
|
|
1566
1752
|
const ttsrSettings = this.#ttsrManager?.getSettings();
|
|
1567
|
-
if (ttsrSettings?.contextMode === "discard") {
|
|
1568
|
-
// Remove the partial/aborted assistant turn from agent state
|
|
1753
|
+
if (ttsrSettings?.contextMode === "discard" && targetAssistantIndex !== -1) {
|
|
1754
|
+
// Remove the partial/aborted assistant turn from agent state when it was persisted.
|
|
1569
1755
|
this.agent.replaceMessages(this.agent.state.messages.slice(0, targetAssistantIndex));
|
|
1570
1756
|
}
|
|
1571
1757
|
// Inject TTSR rules as system reminder before retry
|
|
@@ -2752,6 +2938,7 @@ export class AgentSession {
|
|
|
2752
2938
|
#syncAgentSessionId(sessionId?: string): void {
|
|
2753
2939
|
const sid = this.#providerSessionId ?? sessionId ?? this.sessionManager.getSessionId();
|
|
2754
2940
|
this.agent.sessionId = sid;
|
|
2941
|
+
this.agent.providerSessionId = this.#providerCacheSessionId ?? sid;
|
|
2755
2942
|
this.agent.setMetadataResolver((provider: string) =>
|
|
2756
2943
|
buildSessionMetadata(sid, provider, this.#modelRegistry.authStorage),
|
|
2757
2944
|
);
|
|
@@ -4087,18 +4274,19 @@ export class AgentSession {
|
|
|
4087
4274
|
const details = message.details;
|
|
4088
4275
|
if (!details || typeof details !== "object") return;
|
|
4089
4276
|
const name = (details as { name?: unknown }).name;
|
|
4090
|
-
if (typeof name !== "string" || !
|
|
4091
|
-
const
|
|
4277
|
+
if (typeof name !== "string" || !name.trim()) return;
|
|
4278
|
+
const skill = name.trim();
|
|
4092
4279
|
const sessionId = this.sessionManager.getSessionId();
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4280
|
+
// Canonical GJC workflow skills (deep-interview, ralplan, ultragoal, team)
|
|
4281
|
+
// own their `.gjc/state/skill-active-state.json` row through the
|
|
4282
|
+
// `gjc state handoff` and `gjc state clear` runtime verbs. The prompt
|
|
4283
|
+
// observer here used to overwrite the row with `phase: running` and
|
|
4284
|
+
// later remove it with `active:false`, which clobbered handoff lineage
|
|
4285
|
+
// (`handoff_from`/`handoff_at`) and made the HUD inconsistent with
|
|
4286
|
+
// mode-state. The observational filesystem write is now skipped for
|
|
4287
|
+
// canonical skills; the in-memory `#activeSkillState` tracking below
|
|
4288
|
+
// keeps `getActiveSkillState` accurate for the chain guard.
|
|
4289
|
+
this.#activeSkillState = active ? { skill, sessionId } : undefined;
|
|
4102
4290
|
}
|
|
4103
4291
|
|
|
4104
4292
|
async #syncSkillPromptActiveStateSafely(
|
|
@@ -5762,6 +5950,19 @@ export class AgentSession {
|
|
|
5762
5950
|
}
|
|
5763
5951
|
}
|
|
5764
5952
|
|
|
5953
|
+
async prepareContributionPrep(options: ContributionPrepOptions = {}): Promise<ContributionPrepResult> {
|
|
5954
|
+
return prepareContributionPrep(
|
|
5955
|
+
{
|
|
5956
|
+
sessionId: this.sessionId,
|
|
5957
|
+
cwd: this.sessionManager.getCwd(),
|
|
5958
|
+
sessionFile: this.sessionFile,
|
|
5959
|
+
messages: this.agent.state.messages,
|
|
5960
|
+
customInstructions: options.customInstructions,
|
|
5961
|
+
},
|
|
5962
|
+
options,
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
|
|
5765
5966
|
/**
|
|
5766
5967
|
* Check if context maintenance or promotion is needed and run it.
|
|
5767
5968
|
* Called after agent_end and before prompt submission.
|
|
@@ -6129,7 +6330,7 @@ export class AgentSession {
|
|
|
6129
6330
|
|
|
6130
6331
|
#closeCodexProviderSessionsForHistoryRewrite(): void {
|
|
6131
6332
|
const currentModel = this.model;
|
|
6132
|
-
if (
|
|
6333
|
+
if (currentModel?.api !== "openai-codex-responses") return;
|
|
6133
6334
|
this.#closeProviderSessionsForModelSwitch(currentModel, currentModel);
|
|
6134
6335
|
}
|
|
6135
6336
|
|
|
@@ -8210,7 +8411,7 @@ export class AgentSession {
|
|
|
8210
8411
|
const previousSessionFile = this.sessionFile;
|
|
8211
8412
|
const selectedEntry = this.sessionManager.getEntry(entryId);
|
|
8212
8413
|
|
|
8213
|
-
if (
|
|
8414
|
+
if (selectedEntry?.type !== "message" || selectedEntry.message.role !== "user") {
|
|
8214
8415
|
throw new Error("Invalid entry ID for branching");
|
|
8215
8416
|
}
|
|
8216
8417
|
|