@gajae-code/coding-agent 0.12.18 → 0.12.20
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 +26 -9
- package/dist/types/commands/gc.d.ts +4 -0
- package/dist/types/commands/sdk.d.ts +66 -0
- package/dist/types/config/settings-schema.d.ts +34 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +97 -1
- package/dist/types/modes/acp/acp-agent.d.ts +33 -1
- package/dist/types/modes/components/bash-execution.d.ts +7 -0
- package/dist/types/modes/components/eval-execution.d.ts +7 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +10 -1
- package/dist/types/sdk/acp/adapter.d.ts +9 -17
- package/dist/types/sdk/acp/index.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +93 -1
- package/dist/types/sdk/broker/ensure.d.ts +17 -0
- package/dist/types/sdk/broker/index.d.ts +1 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +10 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +81 -2
- package/dist/types/sdk/broker/startup-budget.d.ts +44 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +7 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +11 -1
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +20 -9
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -6
- package/dist/types/sdk/bus/slack-daemon.d.ts +2 -2
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -2
- package/dist/types/sdk/host/query/revision-store.d.ts +5 -0
- package/dist/types/sdk/host/session-runtime.d.ts +4 -0
- package/dist/types/sdk/lifecycle-session.d.ts +13 -2
- package/dist/types/sdk/prompt-failure.d.ts +13 -0
- package/dist/types/sdk/prompt-watchdog.d.ts +82 -0
- package/dist/types/sdk/session-reconnect.d.ts +20 -0
- package/dist/types/sdk/startup-capability.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +14 -4
- package/dist/types/session/blob-store.d.ts +32 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +40 -2
- package/dist/types/session/internal/session-memory-sidecar.d.ts +881 -0
- package/dist/types/session/session-manager.d.ts +301 -27
- package/dist/types/session/session-storage.d.ts +170 -0
- package/dist/types/task/executor.d.ts +2 -1
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tui/output-block.d.ts +6 -0
- package/package.json +8 -8
- package/src/cli/args.ts +75 -34
- package/src/commands/acp.ts +7 -1
- package/src/commands/gc.ts +31 -2
- package/src/commands/sdk.ts +141 -7
- package/src/config/settings-schema.ts +43 -0
- package/src/coordinator-mcp/server.ts +6 -1
- package/src/export/html/index.ts +83 -39
- package/src/gjc-runtime/gc-runtime.ts +1283 -4
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/local-protocol.ts +14 -0
- package/src/main.ts +40 -9
- package/src/modes/acp/acp-agent.ts +564 -37
- package/src/modes/acp/terminal-auth.ts +2 -2
- package/src/modes/components/bash-execution.ts +14 -0
- package/src/modes/components/eval-execution.ts +14 -0
- package/src/modes/controllers/command-controller.ts +63 -8
- package/src/modes/utils/ui-helpers.ts +81 -11
- package/src/sdk/acp/adapter.ts +46 -45
- package/src/sdk/acp/index.ts +1 -1
- package/src/sdk/broker/broker.ts +392 -11
- package/src/sdk/broker/ensure.ts +131 -61
- package/src/sdk/broker/index.ts +1 -0
- package/src/sdk/broker/lifecycle-ledger.ts +59 -5
- package/src/sdk/broker/lifecycle.ts +278 -63
- package/src/sdk/broker/startup-budget.ts +103 -0
- package/src/sdk/bus/chat-daemon-control.ts +9 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +565 -29
- package/src/sdk/bus/conversation-store.ts +1 -1
- package/src/sdk/bus/discord-daemon.ts +2 -1
- package/src/sdk/bus/discord-live-provider.ts +34 -6
- package/src/sdk/bus/index.ts +118 -20
- package/src/sdk/bus/kind-aware-reconciliation.ts +2 -1
- package/src/sdk/bus/prompt-reconciliation.ts +4 -9
- package/src/sdk/bus/slack-daemon.ts +91 -18
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -2
- package/src/sdk/bus/topic-registry.ts +7 -1
- package/src/sdk/cli/session-cli.ts +8 -1
- package/src/sdk/host/query/handlers.ts +1 -1
- package/src/sdk/host/query/revision-store.ts +48 -12
- package/src/sdk/host/session-runtime.test.ts +50 -0
- package/src/sdk/host/session-runtime.ts +12 -2
- package/src/sdk/lifecycle-session.ts +26 -3
- package/src/sdk/mcp/server.ts +6 -1
- package/src/sdk/prompt-failure.ts +42 -0
- package/src/sdk/prompt-watchdog.ts +162 -0
- package/src/sdk/session-reconnect.ts +43 -0
- package/src/sdk/startup-capability.ts +13 -4
- package/src/session/agent-session.ts +525 -222
- package/src/session/blob-store.ts +89 -0
- package/src/session/internal/managed-session-scope.ts +36 -26
- package/src/session/internal/managed-session-storage.ts +303 -32
- package/src/session/internal/session-memory-sidecar.ts +2160 -0
- package/src/session/session-manager.ts +7027 -394
- package/src/session/session-storage.ts +1134 -11
- package/src/task/executor.ts +17 -4
- package/src/tools/bash.ts +109 -7
- package/src/tools/find.ts +55 -26
- package/src/tools/todo-contract.ts +40 -8
- package/src/tui/output-block.ts +11 -1
|
@@ -16,6 +16,22 @@ export interface SessionStorageSnapshot {
|
|
|
16
16
|
bytes: Uint8Array;
|
|
17
17
|
stat: SessionStorageStat;
|
|
18
18
|
}
|
|
19
|
+
export interface SessionStorageExactReplacementExpectation {
|
|
20
|
+
readonly stat: SessionStorageStat;
|
|
21
|
+
readonly sha256: string;
|
|
22
|
+
}
|
|
23
|
+
/** Upper bound for one descriptor-validated recorded range read. */
|
|
24
|
+
export declare const SESSION_RANGE_READ_MAX_BYTES: number;
|
|
25
|
+
/**
|
|
26
|
+
* One bounded recorded-length read validated against a single opened descriptor.
|
|
27
|
+
* `bytes` is exactly `length` bytes from `[start, start + length)` of the same
|
|
28
|
+
* regular-file object; `stat` is the fresh post-read descriptor snapshot so the
|
|
29
|
+
* caller can compare dev/ino/nlink against the pathname before committing an index.
|
|
30
|
+
*/
|
|
31
|
+
export interface SessionStorageRangeSnapshot {
|
|
32
|
+
stat: SessionStorageStat;
|
|
33
|
+
bytes: Uint8Array;
|
|
34
|
+
}
|
|
19
35
|
/**
|
|
20
36
|
* Four-state writer close lifecycle. Only a successful underlying close confirms
|
|
21
37
|
* `closed`. A failure certified to have happened BEFORE the OS close was dispatched
|
|
@@ -93,6 +109,10 @@ export interface SessionStorageWriter {
|
|
|
93
109
|
writeLineSync(line: string): void;
|
|
94
110
|
flush(): Promise<void>;
|
|
95
111
|
fsync(): Promise<void>;
|
|
112
|
+
/** Synchronously fsync all prior writes when the backend supports durable sidecar publication. */
|
|
113
|
+
fsyncSync?(): void;
|
|
114
|
+
/** Descriptor-bound identity captured from the still-open writer after fsync. */
|
|
115
|
+
statSync?(): SessionStorageStat;
|
|
96
116
|
close(): Promise<void>;
|
|
97
117
|
/**
|
|
98
118
|
* Synchronously close the underlying descriptor. The certainty-aware close
|
|
@@ -107,6 +127,45 @@ export interface SessionStorageWriter {
|
|
|
107
127
|
/** Stored error for non-success close states (`close_failed_retryable`/`close_unknown`). */
|
|
108
128
|
getCloseError(): Error | undefined;
|
|
109
129
|
}
|
|
130
|
+
/** Upper bound for one staged line (excluding the trailing newline). */
|
|
131
|
+
export declare const STAGED_WRITER_LINE_MAX_BYTES: number;
|
|
132
|
+
/** Upper bound for aggregated different-length patches buffered for the publish-time overlay pass. */
|
|
133
|
+
export declare const STAGED_WRITER_PATCH_LIMIT_BYTES: number;
|
|
134
|
+
export declare const STAGED_WRITER_PATCH_MAX_COUNT = 65536;
|
|
135
|
+
export declare const STAGED_MEMORY_WRITER_MAX_BYTES: number;
|
|
136
|
+
export declare const STAGED_MEMORY_WRITER_MAX_LINES: number;
|
|
137
|
+
/**
|
|
138
|
+
* Bounded staged streaming writer for one immutable one-shot destination (fork /
|
|
139
|
+
* capture). Lines are streamed to a sibling staging file; {@link publishNoReplace}
|
|
140
|
+
* atomically publishes the staged file only while the destination is still absent,
|
|
141
|
+
* so publication never materializes the whole file in memory. Different-length
|
|
142
|
+
* {@link patchLine} replacements are buffered (bounded) and applied by a second
|
|
143
|
+
* bounded streaming pass at publish time.
|
|
144
|
+
*
|
|
145
|
+
* `publishNoReplace` is reserved for immutable one-shot destinations and must never
|
|
146
|
+
* be used for the mutable `.spill.commit` marker (checked create/replace helpers
|
|
147
|
+
* exist for that path).
|
|
148
|
+
*/
|
|
149
|
+
export interface StagedStreamingWriter {
|
|
150
|
+
/** Append one complete line; the writer adds the trailing newline. */
|
|
151
|
+
writeLine(bytes: Uint8Array): void;
|
|
152
|
+
/** Move the line cursor to `ordinal` (0-based) so a later patchLine targets it. */
|
|
153
|
+
seekToLine(ordinal: number): void;
|
|
154
|
+
/**
|
|
155
|
+
* Replace the line at `ordinal` with `bytes`. Same-length replacements are
|
|
156
|
+
* applied in place; different-length replacements are buffered (bounded) and
|
|
157
|
+
* applied by the publish-time overlay pass.
|
|
158
|
+
*/
|
|
159
|
+
patchLine(ordinal: number, bytes: Uint8Array): void;
|
|
160
|
+
/** Hand buffered writes to the kernel (the staged descriptor is unbuffered). */
|
|
161
|
+
flush(): void;
|
|
162
|
+
/** Synchronize the staged file. */
|
|
163
|
+
fsync(): void;
|
|
164
|
+
/** Close the staged descriptor; required before {@link publishNoReplace}. */
|
|
165
|
+
closeSync(): void;
|
|
166
|
+
/** Atomically publish the staged file at the destination only while it is absent. */
|
|
167
|
+
publishNoReplace(): void;
|
|
168
|
+
}
|
|
110
169
|
export interface SessionStorage {
|
|
111
170
|
ensureDirSync(dir: string): void;
|
|
112
171
|
existsSync(path: string): boolean;
|
|
@@ -134,6 +193,8 @@ export interface SessionStorage {
|
|
|
134
193
|
writeText(path: string, content: string): Promise<void>;
|
|
135
194
|
rename(path: string, nextPath: string): Promise<void>;
|
|
136
195
|
renameSync(path: string, nextPath: string): void;
|
|
196
|
+
/** Replace only while the destination still has the expected exact identity and bytes. */
|
|
197
|
+
replaceExactSync?(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean;
|
|
137
198
|
unlink(path: string): Promise<void>;
|
|
138
199
|
unlinkSync(path: string): void;
|
|
139
200
|
deleteSessionWithArtifacts(sessionPath: string): Promise<void>;
|
|
@@ -145,6 +206,12 @@ export interface SessionStorage {
|
|
|
145
206
|
*/
|
|
146
207
|
deleteSessionVerified?(target: VerifiedSessionDeleteTarget): Promise<VerifiedSessionDeleteResult>;
|
|
147
208
|
openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter;
|
|
209
|
+
/** Bounded recorded-length read with descriptor identity validation (additive). */
|
|
210
|
+
readRangeSync?(path: string, start: number, length: number): SessionStorageRangeSnapshot;
|
|
211
|
+
/** Async bounded recorded-length read with descriptor identity validation (additive). */
|
|
212
|
+
readRange?(path: string, start: number, length: number): Promise<SessionStorageRangeSnapshot>;
|
|
213
|
+
/** Open a staged streaming writer for one immutable one-shot destination (additive). */
|
|
214
|
+
openStagedWriter?(path: string, options?: SessionStorageWriterOpenOptions): StagedStreamingWriter;
|
|
148
215
|
}
|
|
149
216
|
/** Exact authorization evidence for a transcript or artifact path. */
|
|
150
217
|
export interface SessionStorageFileIdentity {
|
|
@@ -277,6 +344,42 @@ export type NativeDirectoryTreeSnapshot = {
|
|
|
277
344
|
rootIno: string;
|
|
278
345
|
entries: NativeDirectoryTreeEntry[];
|
|
279
346
|
};
|
|
347
|
+
/** Physically observed commit-marker state; corrupt JSON is still `present`. */
|
|
348
|
+
export type SessionCommitMarkerState = {
|
|
349
|
+
kind: "missing";
|
|
350
|
+
} | {
|
|
351
|
+
kind: "present";
|
|
352
|
+
rawBytesSha256: string;
|
|
353
|
+
stat: SessionStorageStat;
|
|
354
|
+
};
|
|
355
|
+
/** Exact `present` expectation for one checked commit-marker replacement. */
|
|
356
|
+
export interface SessionCommitMarkerPresentExpectation {
|
|
357
|
+
/** SHA-256 of the exact raw marker bytes physically on disk (corrupt JSON included). */
|
|
358
|
+
rawBytesSha256: string;
|
|
359
|
+
/** Descriptor snapshot of the marker object expected to be replaced. */
|
|
360
|
+
descriptorIdentity: SessionStorageStat;
|
|
361
|
+
}
|
|
362
|
+
/** Snapshot one commit marker's physical state without granting write authority. */
|
|
363
|
+
export declare function readSessionCommitMarkerSync(storage: SessionStorage, markerPath: string): SessionCommitMarkerState;
|
|
364
|
+
/**
|
|
365
|
+
* Checked commit-marker create: publishes only while the marker is still `missing`.
|
|
366
|
+
* Temp + fsync + atomic create-if-absent + directory fsync (file backend); the
|
|
367
|
+
* in-memory backend mirrors the same missing-expectation abort. Leftover temps are
|
|
368
|
+
* removed on any failure. Runs inside the caller's persistence fence.
|
|
369
|
+
*/
|
|
370
|
+
export declare function createSessionCommitMarkerCheckedSync(storage: SessionStorage, markerPath: string, bytes: Uint8Array, options?: {
|
|
371
|
+
securityContext?: SessionStorageSecurityContext;
|
|
372
|
+
}): void;
|
|
373
|
+
/**
|
|
374
|
+
* Checked commit-marker replace: replaces only on an exact `present` raw/hash +
|
|
375
|
+
* descriptor identity match (corrupt-present included). Temp + fsync + checked
|
|
376
|
+
* atomic rename + directory fsync (file backend); any mismatch aborts with the
|
|
377
|
+
* current marker untouched. The in-memory backend mirrors the same aborts. Runs
|
|
378
|
+
* inside the caller's persistence fence.
|
|
379
|
+
*/
|
|
380
|
+
export declare function replaceSessionCommitMarkerCheckedSync(storage: SessionStorage, markerPath: string, bytes: Uint8Array, expected: SessionCommitMarkerPresentExpectation, options?: {
|
|
381
|
+
securityContext?: SessionStorageSecurityContext;
|
|
382
|
+
}): void;
|
|
280
383
|
export declare class FileSessionStorage implements SessionStorage {
|
|
281
384
|
#private;
|
|
282
385
|
ensureDirSync(dir: string): void;
|
|
@@ -285,6 +388,15 @@ export declare class FileSessionStorage implements SessionStorage {
|
|
|
285
388
|
readTextSync(fpath: string): string;
|
|
286
389
|
readBytesSync(fpath: string): Uint8Array;
|
|
287
390
|
readSnapshotSync(fpath: string): SessionStorageSnapshot;
|
|
391
|
+
/**
|
|
392
|
+
* Bounded recorded-length read with descriptor identity validation: opens one
|
|
393
|
+
* no-follow descriptor, verifies the requested range is fully present, reads
|
|
394
|
+
* exactly `length` bytes, and revalidates dev/ino/nlink on the same descriptor
|
|
395
|
+
* plus the pathname (append-only size growth is tolerated; an object swap is
|
|
396
|
+
* rejected). No path-based Bun Blob reads for managed authority.
|
|
397
|
+
*/
|
|
398
|
+
readRangeSync(fpath: string, start: number, length: number): SessionStorageRangeSnapshot;
|
|
399
|
+
readRange(fpath: string, start: number, length: number): Promise<SessionStorageRangeSnapshot>;
|
|
288
400
|
statSync(path: string): SessionStorageStat;
|
|
289
401
|
listFilesSync(dir: string, pattern: string): string[];
|
|
290
402
|
listFilesByMtime(dir: string, pattern: string): Promise<Array<{
|
|
@@ -298,9 +410,11 @@ export declare class FileSessionStorage implements SessionStorage {
|
|
|
298
410
|
writeText(path: string, content: string): Promise<void>;
|
|
299
411
|
rename(path: string, nextPath: string): Promise<void>;
|
|
300
412
|
renameSync(path: string, nextPath: string): void;
|
|
413
|
+
replaceExactSync(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean;
|
|
301
414
|
unlink(path: string): Promise<void>;
|
|
302
415
|
unlinkSync(path: string): void;
|
|
303
416
|
openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter;
|
|
417
|
+
openStagedWriter(path: string, options?: SessionStorageWriterOpenOptions): StagedStreamingWriter;
|
|
304
418
|
/**
|
|
305
419
|
* Delete a session and sibling artifacts in an operator-selected explicit directory.
|
|
306
420
|
* Default managed roots use deleteSessionVerified and never call this path.
|
|
@@ -317,10 +431,18 @@ export declare class MemorySessionStorage implements SessionStorage {
|
|
|
317
431
|
#private;
|
|
318
432
|
ensureDirSync(_dir: string): void;
|
|
319
433
|
existsSync(path: string): boolean;
|
|
434
|
+
writeBytesOwnedSync(path: string, content: Buffer): void;
|
|
320
435
|
writeTextSync(path: string, content: string): void;
|
|
321
436
|
readTextSync(path: string): string;
|
|
322
437
|
readBytesSync(path: string): Uint8Array;
|
|
323
438
|
readSnapshotSync(path: string): SessionStorageSnapshot;
|
|
439
|
+
/**
|
|
440
|
+
* Bounded recorded-length read with descriptor identity validation: mirrors the
|
|
441
|
+
* file backend's contract (dev/ino/nlink identity, exact `length` bytes present)
|
|
442
|
+
* against the in-memory file model so parity tests can compare backends.
|
|
443
|
+
*/
|
|
444
|
+
readRangeSync(path: string, start: number, length: number): SessionStorageRangeSnapshot;
|
|
445
|
+
readRange(path: string, start: number, length: number): Promise<SessionStorageRangeSnapshot>;
|
|
324
446
|
statSync(path: string): SessionStorageStat;
|
|
325
447
|
listFilesSync(dir: string, pattern: string): string[];
|
|
326
448
|
listFilesByMtime(dir: string, pattern: string): Promise<Array<{
|
|
@@ -334,10 +456,58 @@ export declare class MemorySessionStorage implements SessionStorage {
|
|
|
334
456
|
writeText(path: string, content: string): Promise<void>;
|
|
335
457
|
rename(path: string, nextPath: string): Promise<void>;
|
|
336
458
|
renameSync(path: string, nextPath: string): void;
|
|
459
|
+
replaceExactSync(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean;
|
|
337
460
|
unlink(path: string): Promise<void>;
|
|
338
461
|
unlinkSync(path: string): void;
|
|
339
462
|
deleteSessionWithArtifacts(sessionPath: string): Promise<void>;
|
|
340
463
|
deleteSessionVerified(target: VerifiedSessionDeleteTarget): Promise<VerifiedSessionDeleteResult>;
|
|
341
464
|
openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter;
|
|
465
|
+
openStagedWriter(path: string): StagedStreamingWriter;
|
|
342
466
|
}
|
|
467
|
+
/**
|
|
468
|
+
* Outcome of a disk-retention retirement attempt.
|
|
469
|
+
*
|
|
470
|
+
* `kept` means the transcript survived and nothing of the session was
|
|
471
|
+
* destroyed, with the exact reason so `gjc gc --disk` can report it.
|
|
472
|
+
* `cleanup_pending` means the delete authority already detached or removed the
|
|
473
|
+
* session's artifact tree (or quarantined the transcript) before it stopped:
|
|
474
|
+
* the record survives, the session does not, and a caller must NOT report that
|
|
475
|
+
* as an ordinary keep.
|
|
476
|
+
*/
|
|
477
|
+
export type SessionRetirementOutcome = {
|
|
478
|
+
kind: "retired";
|
|
479
|
+
} | {
|
|
480
|
+
kind: "kept";
|
|
481
|
+
reason: string;
|
|
482
|
+
} | {
|
|
483
|
+
kind: "cleanup_pending";
|
|
484
|
+
reason: string;
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Non-mutating projection of {@link retireSessionTranscript}: would the
|
|
488
|
+
* retention pass's own preconditions let this transcript be retired at all?
|
|
489
|
+
*
|
|
490
|
+
* `gjc gc --disk` runs it so a dry run reports the verdict a prune would reach
|
|
491
|
+
* instead of promising bytes the delete authority will refuse to release. The
|
|
492
|
+
* authority's verdict (containment, identity, artifact tree) is deliberately
|
|
493
|
+
* not predicted here — it is re-derived against live state at delete time.
|
|
494
|
+
*/
|
|
495
|
+
export declare function probeSessionRetirement(storage: FileSessionStorage, sessionsRoot: string, transcriptPath: string): {
|
|
496
|
+
kind: "retirable";
|
|
497
|
+
} | {
|
|
498
|
+
kind: "kept";
|
|
499
|
+
reason: string;
|
|
500
|
+
};
|
|
501
|
+
/**
|
|
502
|
+
* Retire one managed session transcript through the verified hard-delete
|
|
503
|
+
* authority.
|
|
504
|
+
*
|
|
505
|
+
* This is the only supported way for a retention pass to remove a transcript:
|
|
506
|
+
* identity, containment, header id/cwd, artifact tree and parent directory are
|
|
507
|
+
* all re-verified inside {@link FileSessionStorage.deleteSessionVerified}, and
|
|
508
|
+
* anything ambiguous fails closed as `kept` rather than deleting bytes. The
|
|
509
|
+
* caller owns the retention policy; this function owns nothing but the delete
|
|
510
|
+
* authority.
|
|
511
|
+
*/
|
|
512
|
+
export declare function retireSessionTranscript(storage: FileSessionStorage, sessionsRoot: string, transcriptPath: string): Promise<SessionRetirementOutcome>;
|
|
343
513
|
export {};
|
|
@@ -16,6 +16,7 @@ import type { ForkContextSeed } from "../session/agent-session";
|
|
|
16
16
|
import type { ArtifactManager } from "../session/artifacts";
|
|
17
17
|
import type { AuthStorage } from "../session/auth-storage";
|
|
18
18
|
import { SessionManager } from "../session/session-manager";
|
|
19
|
+
import "../tools/yield";
|
|
19
20
|
import type { ContextFileEntry } from "../tools";
|
|
20
21
|
import type { EventBus } from "../utils/event-bus";
|
|
21
22
|
import type { WorkspaceTree } from "../workspace-tree";
|
|
@@ -112,7 +113,7 @@ export interface ExecutorOptions {
|
|
|
112
113
|
export declare class ManagedTaskPersistence {
|
|
113
114
|
#private;
|
|
114
115
|
constructor(artifacts: ArtifactManager, taskId: string);
|
|
115
|
-
openSession(cwd: string): Promise<SessionManager>;
|
|
116
|
+
openSession(cwd: string, sessionMemoryMode?: "off" | "shadow" | "enabled"): Promise<SessionManager>;
|
|
116
117
|
publishOutput(rawOutput: string, metadata: Uint8Array): Promise<void>;
|
|
117
118
|
}
|
|
118
119
|
export declare function createManagedTaskPersistence(artifacts: ArtifactManager, taskId: string): ManagedTaskPersistence;
|
|
@@ -118,6 +118,7 @@ export interface ShellRendererConfig<TArgs> {
|
|
|
118
118
|
resolveCommand?: (args: TArgs | undefined) => string | undefined;
|
|
119
119
|
resolveCwd?: (args: TArgs | undefined) => string | undefined;
|
|
120
120
|
resolveEnv?: (args: TArgs | undefined) => Record<string, string> | undefined;
|
|
121
|
+
compactCommand?: boolean;
|
|
121
122
|
}
|
|
122
123
|
export declare function getBashEnvForDisplay(args: BashRenderArgs): Record<string, string> | undefined;
|
|
123
124
|
export declare function formatBashCommand(args: BashRenderArgs): string;
|
|
@@ -11,6 +11,12 @@ export interface OutputBlockOptions {
|
|
|
11
11
|
width: number;
|
|
12
12
|
applyBg?: boolean;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Return the width available to section content after the output block's
|
|
16
|
+
* visible left and right borders. Keep callers on the same layout contract as
|
|
17
|
+
* renderOutputBlock so width-aware projections are not wrapped a second time.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getOutputBlockContentWidth(width: number, theme: Theme): number;
|
|
14
20
|
export declare function renderOutputBlock(options: OutputBlockOptions, theme: Theme): string[];
|
|
15
21
|
/**
|
|
16
22
|
* Cached wrapper around `renderOutputBlock`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.12.
|
|
4
|
+
"version": "0.12.20",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"@agentclientprotocol/sdk": "1.3.0",
|
|
55
55
|
"@babel/parser": "^7.29.3",
|
|
56
56
|
"@mozilla/readability": "^0.6.0",
|
|
57
|
-
"@gajae-code/stats": "0.12.
|
|
58
|
-
"@gajae-code/agent-core": "0.12.
|
|
59
|
-
"@gajae-code/ai": "0.12.
|
|
60
|
-
"@gajae-code/bridge-client": "0.12.
|
|
61
|
-
"@gajae-code/natives": "0.12.
|
|
62
|
-
"@gajae-code/tui": "0.12.
|
|
63
|
-
"@gajae-code/utils": "0.12.
|
|
57
|
+
"@gajae-code/stats": "0.12.20",
|
|
58
|
+
"@gajae-code/agent-core": "0.12.20",
|
|
59
|
+
"@gajae-code/ai": "0.12.20",
|
|
60
|
+
"@gajae-code/bridge-client": "0.12.20",
|
|
61
|
+
"@gajae-code/natives": "0.12.20",
|
|
62
|
+
"@gajae-code/tui": "0.12.20",
|
|
63
|
+
"@gajae-code/utils": "0.12.20",
|
|
64
64
|
"@puppeteer/browsers": "^2.13.0",
|
|
65
65
|
"@types/turndown": "5.0.6",
|
|
66
66
|
"@xterm/headless": "^6.0.0",
|
package/src/cli/args.ts
CHANGED
|
@@ -80,6 +80,25 @@ function isStartupLoginCommandArg(args: readonly string[], index: number): boole
|
|
|
80
80
|
return argumentCount === 0 || (argumentCount === 1 && !args[index + 1].startsWith("-"));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function takeFlagValue(args: readonly string[], index: number, flag: string, allowDashPrefixed = false): string {
|
|
84
|
+
const value = args[index + 1];
|
|
85
|
+
if (!value || (!allowDashPrefixed && value.startsWith("-"))) {
|
|
86
|
+
throw new CliParseError(`${flag} requires a value`);
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function takePromptValue(
|
|
92
|
+
args: readonly string[],
|
|
93
|
+
index: number,
|
|
94
|
+
flag: string,
|
|
95
|
+
allowInlineDashPrefixed: boolean,
|
|
96
|
+
): string {
|
|
97
|
+
const value = args[index + 1];
|
|
98
|
+
const allowSeparatedDashPrefixed = value === "-" || value?.startsWith("- ") === true;
|
|
99
|
+
return takeFlagValue(args, index, flag, allowInlineDashPrefixed || allowSeparatedDashPrefixed);
|
|
100
|
+
}
|
|
101
|
+
|
|
83
102
|
export function parseArgs(args: string[]): Args {
|
|
84
103
|
const result: Args = {
|
|
85
104
|
messages: [],
|
|
@@ -89,6 +108,7 @@ export function parseArgs(args: string[]): Args {
|
|
|
89
108
|
|
|
90
109
|
for (let i = 0; i < args.length; i++) {
|
|
91
110
|
let arg = args[i];
|
|
111
|
+
let hasInlineValue = false;
|
|
92
112
|
|
|
93
113
|
if (isStartupLoginCommandArg(args, i)) {
|
|
94
114
|
result.authBootstrap = true;
|
|
@@ -106,6 +126,7 @@ export function parseArgs(args: string[]): Args {
|
|
|
106
126
|
const eqIdx = arg.indexOf("=");
|
|
107
127
|
const value = arg.slice(eqIdx + 1);
|
|
108
128
|
arg = arg.slice(0, eqIdx);
|
|
129
|
+
hasInlineValue = true;
|
|
109
130
|
// Insert the value so the existing "args[++i]" logic picks it up
|
|
110
131
|
args.splice(i + 1, 0, value);
|
|
111
132
|
}
|
|
@@ -116,8 +137,9 @@ export function parseArgs(args: string[]): Args {
|
|
|
116
137
|
result.version = true;
|
|
117
138
|
} else if (arg === "--allow-home") {
|
|
118
139
|
result.allowHome = true;
|
|
119
|
-
} else if (arg === "--mode"
|
|
120
|
-
const mode = args
|
|
140
|
+
} else if (arg === "--mode") {
|
|
141
|
+
const mode = takeFlagValue(args, i, "--mode");
|
|
142
|
+
i++;
|
|
121
143
|
if (mode === "text" || mode === "json" || mode === "acp") {
|
|
122
144
|
result.mode = mode;
|
|
123
145
|
} else {
|
|
@@ -137,34 +159,34 @@ export function parseArgs(args: string[]): Args {
|
|
|
137
159
|
} else {
|
|
138
160
|
result.resume = true;
|
|
139
161
|
}
|
|
140
|
-
} else if (arg === "--fork"
|
|
141
|
-
result.fork = args
|
|
142
|
-
} else if (arg === "--provider"
|
|
143
|
-
result.provider = args
|
|
144
|
-
} else if (arg === "--model"
|
|
145
|
-
result.model = args
|
|
146
|
-
} else if (arg === "--smol"
|
|
147
|
-
result.smol = args
|
|
148
|
-
} else if (arg === "--slow"
|
|
149
|
-
result.slow = args
|
|
150
|
-
} else if (arg === "--plan"
|
|
151
|
-
result.plan = args
|
|
152
|
-
} else if (arg === "--mpreset"
|
|
153
|
-
result.mpreset = args
|
|
162
|
+
} else if (arg === "--fork") {
|
|
163
|
+
result.fork = takeFlagValue(args, i++, "--fork");
|
|
164
|
+
} else if (arg === "--provider") {
|
|
165
|
+
result.provider = takeFlagValue(args, i++, "--provider");
|
|
166
|
+
} else if (arg === "--model") {
|
|
167
|
+
result.model = takeFlagValue(args, i++, "--model");
|
|
168
|
+
} else if (arg === "--smol") {
|
|
169
|
+
result.smol = takeFlagValue(args, i++, "--smol");
|
|
170
|
+
} else if (arg === "--slow") {
|
|
171
|
+
result.slow = takeFlagValue(args, i++, "--slow");
|
|
172
|
+
} else if (arg === "--plan") {
|
|
173
|
+
result.plan = takeFlagValue(args, i++, "--plan");
|
|
174
|
+
} else if (arg === "--mpreset") {
|
|
175
|
+
result.mpreset = takeFlagValue(args, i++, "--mpreset");
|
|
154
176
|
} else if (arg === "--default") {
|
|
155
177
|
result.default = true;
|
|
156
|
-
} else if (arg === "--api-key"
|
|
157
|
-
result.apiKey = args
|
|
178
|
+
} else if (arg === "--api-key") {
|
|
179
|
+
result.apiKey = takeFlagValue(args, i++, "--api-key");
|
|
158
180
|
} else if (arg === "--credential") {
|
|
159
181
|
const next = args[i + 1];
|
|
160
182
|
if (!next || next.startsWith("-")) {
|
|
161
183
|
throw new CliParseError("--credential requires <selector>");
|
|
162
184
|
}
|
|
163
185
|
result.credential = args[++i];
|
|
164
|
-
} else if (arg === "--system-prompt"
|
|
165
|
-
result.systemPrompt = args
|
|
166
|
-
} else if (arg === "--append-system-prompt"
|
|
167
|
-
result.appendSystemPrompt = args
|
|
186
|
+
} else if (arg === "--system-prompt") {
|
|
187
|
+
result.systemPrompt = takePromptValue(args, i++, "--system-prompt", hasInlineValue);
|
|
188
|
+
} else if (arg === "--append-system-prompt") {
|
|
189
|
+
result.appendSystemPrompt = takePromptValue(args, i++, "--append-system-prompt", hasInlineValue);
|
|
168
190
|
} else if (arg === "--clipboard-transport") {
|
|
169
191
|
const next = args[i + 1];
|
|
170
192
|
if (!next || next.startsWith("-")) {
|
|
@@ -196,14 +218,16 @@ export function parseArgs(args: string[]): Args {
|
|
|
196
218
|
throw new CliParseError("--mcp-config requires <absolute-path>");
|
|
197
219
|
}
|
|
198
220
|
result.mcpConfig = args[++i];
|
|
199
|
-
} else if (arg === "--provider-session-id"
|
|
200
|
-
result.providerSessionId = args
|
|
221
|
+
} else if (arg === "--provider-session-id") {
|
|
222
|
+
result.providerSessionId = takeFlagValue(args, i++, "--provider-session-id");
|
|
201
223
|
} else if (arg === "--no-session") {
|
|
202
224
|
result.noSession = true;
|
|
203
|
-
} else if (arg === "--session-dir"
|
|
204
|
-
result.sessionDir = args
|
|
205
|
-
} else if (arg === "--models"
|
|
206
|
-
result.models = args
|
|
225
|
+
} else if (arg === "--session-dir") {
|
|
226
|
+
result.sessionDir = takeFlagValue(args, i++, "--session-dir");
|
|
227
|
+
} else if (arg === "--models") {
|
|
228
|
+
result.models = takeFlagValue(args, i++, "--models")
|
|
229
|
+
.split(",")
|
|
230
|
+
.map(s => s.trim());
|
|
207
231
|
} else if (arg === "--no-tools") {
|
|
208
232
|
result.noTools = true;
|
|
209
233
|
} else if (arg === "--no-lsp") {
|
|
@@ -212,8 +236,23 @@ export function parseArgs(args: string[]): Args {
|
|
|
212
236
|
result.noPty = true;
|
|
213
237
|
} else if (arg === "--tmux") {
|
|
214
238
|
result.tmux = true;
|
|
215
|
-
} else if (arg === "--
|
|
216
|
-
const
|
|
239
|
+
} else if (arg === "--extension" || arg === "-e") {
|
|
240
|
+
const extension = takeFlagValue(args, i++, "--extension");
|
|
241
|
+
result.extensions = [...(result.extensions ?? []), extension];
|
|
242
|
+
} else if (arg === "--hook") {
|
|
243
|
+
const hook = takeFlagValue(args, i++, "--hook");
|
|
244
|
+
result.hooks = [...(result.hooks ?? []), hook];
|
|
245
|
+
} else if (arg === "--no-extensions") {
|
|
246
|
+
result.noExtensions = true;
|
|
247
|
+
} else if (arg === "--no-skills") {
|
|
248
|
+
result.noSkills = true;
|
|
249
|
+
} else if (arg === "--skills") {
|
|
250
|
+
result.skills = takeFlagValue(args, i++, "--skills")
|
|
251
|
+
.split(",")
|
|
252
|
+
.map(s => s.trim())
|
|
253
|
+
.filter(Boolean);
|
|
254
|
+
} else if (arg === "--tools") {
|
|
255
|
+
const toolNames = takeFlagValue(args, i++, "--tools")
|
|
217
256
|
.split(",")
|
|
218
257
|
.map(s => s.trim().toLowerCase())
|
|
219
258
|
.filter(Boolean);
|
|
@@ -248,8 +287,8 @@ export function parseArgs(args: string[]): Args {
|
|
|
248
287
|
result.thinking = thinking;
|
|
249
288
|
} else if (arg === "--print" || arg === "-p") {
|
|
250
289
|
result.print = true;
|
|
251
|
-
} else if (arg === "--export"
|
|
252
|
-
result.export = args
|
|
290
|
+
} else if (arg === "--export") {
|
|
291
|
+
result.export = takeFlagValue(args, i++, "--export");
|
|
253
292
|
} else if (arg === "--no-rules") {
|
|
254
293
|
result.noRules = true;
|
|
255
294
|
} else if (arg === "--no-title") {
|
|
@@ -263,13 +302,15 @@ export function parseArgs(args: string[]): Args {
|
|
|
263
302
|
}
|
|
264
303
|
} else if (arg.startsWith("@")) {
|
|
265
304
|
result.fileArgs.push(arg.slice(1)); // Remove @ prefix
|
|
266
|
-
} else if (
|
|
305
|
+
} else if (arg.startsWith("-")) {
|
|
306
|
+
result.unknownFlags.set(arg, true);
|
|
307
|
+
} else {
|
|
267
308
|
result.messages.push(arg);
|
|
268
309
|
}
|
|
269
310
|
}
|
|
270
311
|
|
|
271
312
|
if (result.default && !result.mpreset) {
|
|
272
|
-
throw new
|
|
313
|
+
throw new CliParseError("--default requires --mpreset <name>");
|
|
273
314
|
}
|
|
274
315
|
if (
|
|
275
316
|
result.mcpConfig !== undefined &&
|
package/src/commands/acp.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Thin wrapper around the launch flow that forces `mode: "acp"` unless the
|
|
5
5
|
* ACP terminal-auth flag asks the same command to open the interactive TUI.
|
|
6
6
|
*/
|
|
7
|
-
import { Command } from "@gajae-code/utils/cli";
|
|
7
|
+
import { CliParseError, Command } from "@gajae-code/utils/cli";
|
|
8
8
|
import { parseArgs } from "../cli/args";
|
|
9
9
|
import { runRootCommand } from "../main";
|
|
10
10
|
import { prepareAcpTerminalAuthArgs } from "../modes/acp/terminal-auth";
|
|
@@ -16,6 +16,12 @@ export default class Acp extends Command {
|
|
|
16
16
|
async run(): Promise<void> {
|
|
17
17
|
const { args, terminalAuth } = prepareAcpTerminalAuthArgs(this.argv);
|
|
18
18
|
const parsed = parseArgs(args);
|
|
19
|
+
if (parsed.unknownFlags.size > 0) {
|
|
20
|
+
throw new CliParseError(`Unknown ACP option: ${[...parsed.unknownFlags.keys()].join(", ")}`);
|
|
21
|
+
}
|
|
22
|
+
if (terminalAuth && parsed.mode !== undefined) {
|
|
23
|
+
throw new CliParseError("--acp-terminal-auth only supports --mode acp");
|
|
24
|
+
}
|
|
19
25
|
if (!terminalAuth) {
|
|
20
26
|
parsed.mode = "acp";
|
|
21
27
|
}
|
package/src/commands/gc.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { Command, Flags } from "@gajae-code/utils/cli";
|
|
2
|
-
import {
|
|
2
|
+
import { isSettingsInitialized, settings } from "../config/settings";
|
|
3
|
+
import { type GcDiskPolicy, runGjcGcCommand } from "../gjc-runtime/gc-runtime";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the `gc.*` retention knobs from settings. When settings are not
|
|
7
|
+
* initialized (headless/early invocation) the runtime falls back to its
|
|
8
|
+
* schema-backed defaults, so this returns nothing rather than guessing.
|
|
9
|
+
*/
|
|
10
|
+
function resolveDiskPolicyFromSettings(): Partial<GcDiskPolicy> | undefined {
|
|
11
|
+
if (!isSettingsInitialized()) return undefined;
|
|
12
|
+
return {
|
|
13
|
+
sessions_max_age_days: settings.get("gc.sessions.maxAgeDays"),
|
|
14
|
+
sessions_max_total_bytes: settings.get("gc.sessions.maxTotalBytes"),
|
|
15
|
+
natives_keep_versions: settings.get("gc.natives.keepVersions"),
|
|
16
|
+
backups_max_age_days: settings.get("gc.backups.maxAgeDays"),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
3
19
|
|
|
4
20
|
export default class Gc extends Command {
|
|
5
21
|
static description = "Garbage-collect stale GJC session/PID records (dry-run by default)";
|
|
@@ -9,6 +25,10 @@ export default class Gc extends Command {
|
|
|
9
25
|
prune: Flags.boolean({ description: "Remove stale records (default: report only)", default: false }),
|
|
10
26
|
force: Flags.boolean({ description: "Alias for --prune (eligible records only)", default: false }),
|
|
11
27
|
"dry-run": Flags.boolean({ description: "Force report-only mode", default: false }),
|
|
28
|
+
disk: Flags.boolean({
|
|
29
|
+
description: "Also report on-disk retention (sessions, blobs, natives, backups)",
|
|
30
|
+
default: false,
|
|
31
|
+
}),
|
|
12
32
|
"repair-session-index": Flags.boolean({
|
|
13
33
|
description: "Quarantine a corrupt session-index suffix and retain its valid prefix",
|
|
14
34
|
default: false,
|
|
@@ -20,11 +40,20 @@ export default class Gc extends Command {
|
|
|
20
40
|
"gjc gc --json",
|
|
21
41
|
"gjc gc --prune",
|
|
22
42
|
"gjc gc --prune --json",
|
|
43
|
+
"gjc gc --disk",
|
|
44
|
+
"gjc gc --disk --json",
|
|
45
|
+
"gjc gc --disk --prune",
|
|
23
46
|
"gjc gc --repair-session-index --json",
|
|
24
47
|
];
|
|
25
48
|
|
|
26
49
|
async run(): Promise<void> {
|
|
27
|
-
const result = await runGjcGcCommand(
|
|
50
|
+
const result = await runGjcGcCommand(
|
|
51
|
+
this.argv,
|
|
52
|
+
process.cwd(),
|
|
53
|
+
process.env,
|
|
54
|
+
undefined,
|
|
55
|
+
resolveDiskPolicyFromSettings(),
|
|
56
|
+
);
|
|
28
57
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
29
58
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
30
59
|
process.exitCode = result.status;
|