@gajae-code/coding-agent 0.11.8 → 0.11.9
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 +18 -1
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +12 -5
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +41 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +32 -17
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +17 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +135 -4
- package/dist/types/gjc-runtime/state-writer.d.ts +44 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +5 -3
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/session/internal/managed-session-scope.d.ts +12 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -3
- package/dist/types/session/session-manager.d.ts +5 -1
- package/dist/types/session/session-storage.d.ts +13 -0
- package/dist/types/skill-state/active-state.d.ts +4 -0
- package/package.json +8 -8
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli.ts +32 -0
- package/src/commands/deep-interview.ts +9 -4
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/model-profiles.ts +2 -3
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +78 -163
- package/src/gjc-runtime/deep-interview-ambiguity.ts +68 -47
- package/src/gjc-runtime/deep-interview-draft.ts +842 -0
- package/src/gjc-runtime/deep-interview-payload.ts +262 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +294 -226
- package/src/gjc-runtime/deep-interview-repair.ts +1674 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +264 -103
- package/src/gjc-runtime/deep-interview-state.ts +916 -4
- package/src/gjc-runtime/ralplan-runtime.ts +3 -1
- package/src/gjc-runtime/state-writer.ts +166 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +386 -1
- package/src/gjc-runtime/workflow-manifest.ts +191 -5
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/modes/components/welcome.ts +10 -2
- package/src/modes/interactive-mode.ts +29 -9
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/runtime-mcp/json-rpc.ts +7 -4
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-control.ts +5 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +18 -2
- package/src/session/agent-session.ts +86 -0
- package/src/session/internal/managed-session-scope.ts +206 -111
- package/src/session/internal/managed-session-storage.ts +17 -4
- package/src/session/session-manager.ts +96 -33
- package/src/session/session-storage.ts +23 -0
- package/src/skill-state/active-state.ts +10 -0
- package/src/task/executor.ts +13 -5
- package/src/tools/ask.ts +1 -0
|
@@ -547,6 +547,13 @@ export class SessionMigrationPolicyError extends Error {
|
|
|
547
547
|
}
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
+
export class SessionArtifactCapacityError extends Error {
|
|
551
|
+
constructor(message: string) {
|
|
552
|
+
super(message);
|
|
553
|
+
this.name = "SessionArtifactCapacityError";
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
550
557
|
export type ResumeTailInspection = ResumeTailResumable | ResumeTailTerminal | ResumeTailError;
|
|
551
558
|
export interface StrictSessionOpenSuccess {
|
|
552
559
|
kind: "opened";
|
|
@@ -555,7 +562,8 @@ export interface StrictSessionOpenSuccess {
|
|
|
555
562
|
|
|
556
563
|
export interface StrictSessionOpenFailure {
|
|
557
564
|
kind: "error";
|
|
558
|
-
reason: ResumeTailError["reason"] | "identity-mismatch" | "migration-required";
|
|
565
|
+
reason: ResumeTailError["reason"] | "identity-mismatch" | "migration-required" | "artifact_capacity_exceeded";
|
|
566
|
+
message?: string;
|
|
559
567
|
}
|
|
560
568
|
|
|
561
569
|
/** Exact transcript bytes and authority captured from one descriptor-bound read. */
|
|
@@ -2110,41 +2118,90 @@ export async function recoverOrphanedBackups(sessionDir: string, storage: Sessio
|
|
|
2110
2118
|
}
|
|
2111
2119
|
|
|
2112
2120
|
/**
|
|
2113
|
-
*
|
|
2114
|
-
*
|
|
2115
|
-
*
|
|
2121
|
+
* Returns session metadata sorted by mtime (newest first).
|
|
2122
|
+
*
|
|
2123
|
+
* Directory entries are cheap to stat, but opening every transcript before applying a
|
|
2124
|
+
* small caller limit makes welcome-screen startup scale with the entire session history.
|
|
2125
|
+
* Rank paths first, then read bounded batches until the requested number of valid
|
|
2126
|
+
* sessions has been found. Invalid or future-version files do not prevent older valid
|
|
2127
|
+
* sessions from filling the result.
|
|
2116
2128
|
*/
|
|
2117
|
-
async function getSortedSessions(
|
|
2129
|
+
async function getSortedSessions(
|
|
2130
|
+
sessionDir: string,
|
|
2131
|
+
storage: SessionStorage,
|
|
2132
|
+
limit?: number,
|
|
2133
|
+
): Promise<RecentSessionInfo[]> {
|
|
2118
2134
|
await recoverOrphanedBackups(sessionDir, storage);
|
|
2119
2135
|
try {
|
|
2120
|
-
|
|
2136
|
+
let candidates: Array<{ path: string; mtime: number }> | undefined;
|
|
2137
|
+
if (storage.listFilesByMtime) {
|
|
2138
|
+
try {
|
|
2139
|
+
candidates = (await storage.listFilesByMtime(sessionDir, "*.jsonl")).map(candidate => ({
|
|
2140
|
+
path: candidate.path,
|
|
2141
|
+
mtime: candidate.mtimeMs,
|
|
2142
|
+
}));
|
|
2143
|
+
} catch (error) {
|
|
2144
|
+
logger.warn("Native session mtime listing failed; using JavaScript fallback", {
|
|
2145
|
+
sessionDir,
|
|
2146
|
+
error: toError(error).message,
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
} else {
|
|
2150
|
+
logger.debug("Native session mtime listing unavailable; using JavaScript fallback", { sessionDir });
|
|
2151
|
+
}
|
|
2152
|
+
candidates ??= storage.listFilesSync(sessionDir, "*.jsonl").flatMap(path => {
|
|
2153
|
+
try {
|
|
2154
|
+
return [{ path, mtime: storage.statSync(path).mtimeMs }];
|
|
2155
|
+
} catch {
|
|
2156
|
+
return [];
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
candidates.sort((left, right) => {
|
|
2160
|
+
if (left.mtime !== right.mtime) return right.mtime - left.mtime;
|
|
2161
|
+
return left.path < right.path ? -1 : left.path > right.path ? 1 : 0;
|
|
2162
|
+
});
|
|
2163
|
+
|
|
2121
2164
|
const sessions: RecentSessionInfo[] = [];
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2165
|
+
const batchSize = Math.max(32, limit ?? 0);
|
|
2166
|
+
|
|
2167
|
+
for (
|
|
2168
|
+
let offset = 0;
|
|
2169
|
+
offset < candidates.length && (limit === undefined || sessions.length < limit);
|
|
2170
|
+
offset += batchSize
|
|
2171
|
+
) {
|
|
2172
|
+
const batch = candidates.slice(offset, offset + batchSize);
|
|
2173
|
+
const parsed = await Promise.all(
|
|
2174
|
+
batch.map(async candidate => {
|
|
2175
|
+
try {
|
|
2176
|
+
const buffer = Buffer.allocUnsafe(SESSION_LIST_PREFIX_BYTES);
|
|
2177
|
+
const content = await readSessionListPrefix(candidate.path, storage, buffer);
|
|
2178
|
+
const entries = parseJsonlLenient<Record<string, unknown>>(content);
|
|
2179
|
+
if (entries.length === 0) return undefined;
|
|
2180
|
+
const header = entries[0] as Record<string, unknown>;
|
|
2181
|
+
if (
|
|
2182
|
+
header.type !== "session" ||
|
|
2183
|
+
typeof header.id !== "string" ||
|
|
2184
|
+
!isSupportedSessionVersion(header.version)
|
|
2185
|
+
)
|
|
2186
|
+
return undefined;
|
|
2187
|
+
if (typeof header.version === "number" && header.version >= 4) {
|
|
2188
|
+
for (const patch of await readSessionListTrailingPatches(candidate.path, storage, buffer)) {
|
|
2189
|
+
applySessionListHeaderPatch(header as unknown as SessionListHeader, patch);
|
|
2190
|
+
}
|
|
2139
2191
|
}
|
|
2192
|
+
const firstPrompt = header.title ? undefined : extractFirstUserPrompt(entries);
|
|
2193
|
+
return new RecentSessionInfo(candidate.path, candidate.mtime, header, firstPrompt);
|
|
2194
|
+
} catch {
|
|
2195
|
+
return undefined;
|
|
2140
2196
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2197
|
+
}),
|
|
2198
|
+
);
|
|
2199
|
+
for (const session of parsed) {
|
|
2200
|
+
if (session) sessions.push(session);
|
|
2201
|
+
if (limit !== undefined && sessions.length >= limit) break;
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
return sessions;
|
|
2148
2205
|
} catch {
|
|
2149
2206
|
return [];
|
|
2150
2207
|
}
|
|
@@ -2155,7 +2212,7 @@ export async function findMostRecentSession(
|
|
|
2155
2212
|
sessionDir: string,
|
|
2156
2213
|
storage: SessionStorage = new FileSessionStorage(),
|
|
2157
2214
|
): Promise<string | null> {
|
|
2158
|
-
const sessions = await getSortedSessions(sessionDir, storage);
|
|
2215
|
+
const sessions = await getSortedSessions(sessionDir, storage, 1);
|
|
2159
2216
|
return sessions[0]?.path || null;
|
|
2160
2217
|
}
|
|
2161
2218
|
|
|
@@ -3427,8 +3484,7 @@ export async function getRecentSessions(
|
|
|
3427
3484
|
limit = DEFAULT_WELCOME_RECENT_SESSION_LIMIT,
|
|
3428
3485
|
storage: SessionStorage = new FileSessionStorage(),
|
|
3429
3486
|
): Promise<RecentSessionInfo[]> {
|
|
3430
|
-
|
|
3431
|
-
return sessions.slice(0, limit);
|
|
3487
|
+
return getSortedSessions(sessionDir, storage, limit);
|
|
3432
3488
|
}
|
|
3433
3489
|
|
|
3434
3490
|
/**
|
|
@@ -7299,6 +7355,7 @@ export class SessionManager {
|
|
|
7299
7355
|
if (opened.kind === "error") {
|
|
7300
7356
|
managedDestinationStore.assertBound();
|
|
7301
7357
|
if (opened.code === "legacy_migration_disabled") throw new SessionMigrationPolicyError();
|
|
7358
|
+
if (opened.code === "artifact_capacity_exceeded") throw new SessionArtifactCapacityError(opened.message);
|
|
7302
7359
|
throw new Error(`Could not open managed session: ${opened.message}`);
|
|
7303
7360
|
}
|
|
7304
7361
|
assertManagedDestinationBound();
|
|
@@ -7809,6 +7866,8 @@ export class SessionManager {
|
|
|
7809
7866
|
} catch (error) {
|
|
7810
7867
|
if (error instanceof SessionMigrationPolicyError)
|
|
7811
7868
|
return { kind: "error", reason: "legacy_migration_disabled" };
|
|
7869
|
+
if (error instanceof SessionArtifactCapacityError)
|
|
7870
|
+
return { kind: "error", reason: "artifact_capacity_exceeded", message: error.message };
|
|
7812
7871
|
if (
|
|
7813
7872
|
error instanceof Error &&
|
|
7814
7873
|
(error.message.includes("source_changed") || error.message.includes("changed before migration"))
|
|
@@ -7867,6 +7926,10 @@ export class SessionManager {
|
|
|
7867
7926
|
);
|
|
7868
7927
|
if (opened.kind === "error") {
|
|
7869
7928
|
if (opened.reason === "legacy_migration_disabled") throw new SessionMigrationPolicyError();
|
|
7929
|
+
if (opened.reason === "artifact_capacity_exceeded")
|
|
7930
|
+
throw new SessionArtifactCapacityError(
|
|
7931
|
+
opened.message ?? "Session artifacts exceed the migration capacity.",
|
|
7932
|
+
);
|
|
7870
7933
|
return undefined;
|
|
7871
7934
|
}
|
|
7872
7935
|
return opened.manager;
|
|
@@ -167,6 +167,8 @@ export interface SessionStorage {
|
|
|
167
167
|
readSnapshotSync?(path: string): SessionStorageSnapshot;
|
|
168
168
|
statSync(path: string): SessionStorageStat;
|
|
169
169
|
listFilesSync(dir: string, pattern: string): string[];
|
|
170
|
+
/** List matching files with mtimes without issuing one JavaScript stat call per path. */
|
|
171
|
+
listFilesByMtime?(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>>;
|
|
170
172
|
/**
|
|
171
173
|
* Strict directory scan that never suppresses scan/root errors. Used by strict
|
|
172
174
|
* authorization inventory; the forgiving {@link listFilesSync} stays display-only.
|
|
@@ -705,6 +707,21 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
705
707
|
return [];
|
|
706
708
|
}
|
|
707
709
|
}
|
|
710
|
+
async listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
711
|
+
const result = await native.glob({
|
|
712
|
+
path: dir,
|
|
713
|
+
pattern,
|
|
714
|
+
fileType: native.FileType.File,
|
|
715
|
+
recursive: false,
|
|
716
|
+
hidden: false,
|
|
717
|
+
gitignore: false,
|
|
718
|
+
sortByMtime: true,
|
|
719
|
+
});
|
|
720
|
+
return result.matches.map(match => ({
|
|
721
|
+
path: path.join(dir, match.path),
|
|
722
|
+
mtimeMs: match.mtime ?? 0,
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
708
725
|
|
|
709
726
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
710
727
|
// Strict: never suppress scan/root errors. Authorization inventory depends on
|
|
@@ -1470,6 +1487,12 @@ export class MemorySessionStorage implements SessionStorage {
|
|
|
1470
1487
|
}
|
|
1471
1488
|
return files;
|
|
1472
1489
|
}
|
|
1490
|
+
listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
1491
|
+
const files = this.listFilesSync(dir, pattern)
|
|
1492
|
+
.map(path => ({ path, mtimeMs: this.statSync(path).mtimeMs }))
|
|
1493
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1494
|
+
return Promise.resolve(files);
|
|
1495
|
+
}
|
|
1473
1496
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
1474
1497
|
// In-memory scan never suppresses; identical to the display scan.
|
|
1475
1498
|
return this.listFilesSync(dir, pattern);
|
|
@@ -71,6 +71,8 @@ export interface SkillActiveEntry {
|
|
|
71
71
|
handoff_at?: string;
|
|
72
72
|
active_subskills?: ActiveSubskillEntry[];
|
|
73
73
|
source_state_revision?: number;
|
|
74
|
+
/** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
|
|
75
|
+
committed_mode_state_revision?: number;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
export interface SkillActiveState {
|
|
@@ -114,6 +116,8 @@ export interface SyncSkillActiveStateOptions {
|
|
|
114
116
|
handoff_at?: string;
|
|
115
117
|
active_subskills?: ActiveSubskillEntry[];
|
|
116
118
|
sourceRevision?: number;
|
|
119
|
+
/** Durable ordering token from the source workflow mode-state, not this cache file's revision. */
|
|
120
|
+
committedModeRevision?: number;
|
|
117
121
|
}
|
|
118
122
|
|
|
119
123
|
const HUD_TEXT_LIMIT = 80;
|
|
@@ -785,6 +789,9 @@ async function persistActiveEntry(
|
|
|
785
789
|
await writeActiveEntry(cwd, sessionScope, entry.skill, entry, {
|
|
786
790
|
cwd,
|
|
787
791
|
audit: activeStateWriterAudit("write-active-entry", sessionScope),
|
|
792
|
+
...(typeof entry.committed_mode_state_revision === "number"
|
|
793
|
+
? { orderingRevision: entry.committed_mode_state_revision }
|
|
794
|
+
: {}),
|
|
788
795
|
});
|
|
789
796
|
}
|
|
790
797
|
}
|
|
@@ -863,6 +870,9 @@ export async function syncSkillActiveState(options: SyncSkillActiveStateOptions)
|
|
|
863
870
|
? { active_subskills: preservedActiveSubskills }
|
|
864
871
|
: {}),
|
|
865
872
|
...(typeof options.sourceRevision === "number" ? { source_state_revision: options.sourceRevision } : {}),
|
|
873
|
+
...(typeof options.committedModeRevision === "number"
|
|
874
|
+
? { committed_mode_state_revision: options.committedModeRevision }
|
|
875
|
+
: {}),
|
|
866
876
|
};
|
|
867
877
|
const sessionScope = { sessionId: options.sessionId };
|
|
868
878
|
await removeSupersededPlanningPipelineEntries(options.cwd, sessionScope, entry);
|
package/src/task/executor.ts
CHANGED
|
@@ -486,11 +486,12 @@ function buildSchemaViolationOutcome(
|
|
|
486
486
|
missing.length > 0
|
|
487
487
|
? `schema_violation: missing required fields: ${missing.join(", ")}`
|
|
488
488
|
: `schema_violation: ${failure.message}`;
|
|
489
|
+
const dataPreview = previewOffendingData(data);
|
|
489
490
|
const payload = {
|
|
490
491
|
error: "schema_violation",
|
|
491
492
|
message: failure.message,
|
|
492
493
|
missingRequired: missing,
|
|
493
|
-
data
|
|
494
|
+
data,
|
|
494
495
|
};
|
|
495
496
|
let rawOutput: string;
|
|
496
497
|
try {
|
|
@@ -498,7 +499,7 @@ function buildSchemaViolationOutcome(
|
|
|
498
499
|
} catch {
|
|
499
500
|
rawOutput = `{"error":"schema_violation","message":${JSON.stringify(headline)}}`;
|
|
500
501
|
}
|
|
501
|
-
return { rawOutput, stderr: headline
|
|
502
|
+
return { rawOutput, stderr: `${headline}. Offending data preview: ${dataPreview}`, exitCode: 1 };
|
|
502
503
|
}
|
|
503
504
|
|
|
504
505
|
function buildPlaceholderYieldOutcome(
|
|
@@ -541,9 +542,16 @@ export function finalizeSubprocessOutput(args: FinalizeSubprocessOutputArgs): Fi
|
|
|
541
542
|
const completeData = normalizeCompleteData(submitData, reportFindings);
|
|
542
543
|
const { validator, error: schemaError } = buildOutputValidator(outputSchema);
|
|
543
544
|
if (schemaError) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
const outcome = buildSchemaViolationOutcome(
|
|
546
|
+
{
|
|
547
|
+
message: `invalid output schema: ${schemaError}`,
|
|
548
|
+
missingRequired: [],
|
|
549
|
+
},
|
|
550
|
+
completeData,
|
|
551
|
+
);
|
|
552
|
+
rawOutput = outcome.rawOutput;
|
|
553
|
+
stderr = outcome.stderr;
|
|
554
|
+
exitCode = outcome.exitCode;
|
|
547
555
|
} else {
|
|
548
556
|
const placeholderPath = findPlaceholderYieldPath(completeData);
|
|
549
557
|
if (placeholderPath) {
|
package/src/tools/ask.ts
CHANGED
|
@@ -461,6 +461,7 @@ function recoverRoundZeroIntentContract(
|
|
|
461
461
|
const deepInterview = question.deepInterview;
|
|
462
462
|
const hasIntentContract = Object.hasOwn(deepInterview, "intent_contract");
|
|
463
463
|
const hasIntentReview = Object.hasOwn(deepInterview, "intent_review");
|
|
464
|
+
if (hasIntentContract && hasIntentReview && stage !== "topology") return { outcome: "reject" };
|
|
464
465
|
if (hasIntentContract !== hasIntentReview && askSchema.safeParse(normalizedArguments).success)
|
|
465
466
|
return { outcome: "passthrough" };
|
|
466
467
|
|