@gajae-code/coding-agent 0.12.11 → 0.12.13
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 +68 -0
- package/dist/types/cli/marketplace-hint.d.ts +28 -0
- package/dist/types/cli/notify-cli.d.ts +42 -1
- package/dist/types/cli/update-cli.d.ts +9 -3
- package/dist/types/cli.d.ts +10 -0
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/config/file-lock-gc.d.ts +13 -1
- package/dist/types/config/file-lock.d.ts +3 -0
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/models-config-schema.d.ts +99 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
- package/dist/types/extensibility/extensions/types.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +14 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +11 -0
- package/dist/types/main.d.ts +6 -2
- package/dist/types/modes/acp/acp-agent.d.ts +24 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/utils/host-status.d.ts +23 -0
- package/dist/types/runtime-mcp/types.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
- package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
- package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
- package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
- package/dist/types/sdk/bus/html-format.d.ts +10 -0
- package/dist/types/sdk/bus/notification-service.d.ts +10 -0
- package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
- package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
- package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
- package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +28 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +119 -17
- package/dist/types/sdk/bus/telegram-reference.d.ts +20 -1
- package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
- package/dist/types/sdk/host/host.d.ts +52 -0
- package/dist/types/sdk/lifecycle-session.d.ts +6 -0
- package/dist/types/sdk/session-activation.d.ts +41 -0
- package/dist/types/sdk/session.d.ts +18 -0
- package/dist/types/sdk/startup-capability.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +7 -2
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +40 -6
- package/dist/types/tools/index.d.ts +15 -0
- package/dist/types/utils/npm-registry.d.ts +71 -0
- package/examples/extensions/README.md +38 -4
- package/examples/extensions/ooo-bridge.ts +15 -0
- package/package.json +8 -8
- package/scripts/generate-sdk-operation-inventory.ts +4 -0
- package/src/async/job-manager.ts +9 -2
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/marketplace-hint.ts +39 -0
- package/src/cli/notify-cli.ts +226 -2
- package/src/cli/plugin-cli.ts +10 -0
- package/src/cli/update-cli.ts +90 -23
- package/src/cli.ts +23 -0
- package/src/commands/notify.ts +27 -3
- package/src/commands/sdk.ts +1 -0
- package/src/config/file-lock-gc.ts +65 -26
- package/src/config/file-lock.ts +61 -16
- package/src/config/model-profiles.ts +46 -30
- package/src/config/model-registry.ts +17 -5
- package/src/config/models-config-schema.ts +9 -4
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/question-state.ts +2 -0
- package/src/coordinator-mcp/server.ts +358 -7
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
- package/src/extensibility/extensions/runner.ts +15 -2
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +9 -5
- package/src/gjc-runtime/deep-interview-state.ts +27 -3
- package/src/gjc-runtime/gc-render.ts +8 -1
- package/src/gjc-runtime/gc-runtime.ts +15 -1
- package/src/gjc-runtime/launch-tmux.ts +53 -1
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/main.ts +214 -70
- package/src/modes/DESIGN.md +8 -5
- package/src/modes/acp/acp-agent.ts +123 -7
- package/src/modes/components/footer.ts +10 -18
- package/src/modes/components/model-selector.ts +52 -5
- package/src/modes/components/session-selector.ts +26 -3
- package/src/modes/components/status-line/gh.ts +23 -2
- package/src/modes/controllers/event-controller.ts +3 -0
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +11 -0
- package/src/modes/controllers/selector-controller.ts +8 -2
- package/src/modes/interactive-mode.ts +7 -1
- package/src/modes/print-mode.ts +10 -0
- package/src/modes/utils/host-status.ts +38 -0
- package/src/prompts/system/project-prompt.md +19 -0
- package/src/runtime-mcp/types.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +60 -3
- package/src/sdk/bus/chat-daemon-cli.ts +42 -0
- package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
- package/src/sdk/bus/chat-daemon-control.ts +16 -3
- package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
- package/src/sdk/bus/control-plane-frames.ts +51 -0
- package/src/sdk/bus/conversation-store.ts +29 -1
- package/src/sdk/bus/existing-thread-readiness.ts +155 -0
- package/src/sdk/bus/html-format.ts +19 -3
- package/src/sdk/bus/index.ts +91 -12
- package/src/sdk/bus/notification-service.ts +28 -1
- package/src/sdk/bus/rich-render.ts +3 -2
- package/src/sdk/bus/slack-daemon.ts +99 -4
- package/src/sdk/bus/slack-live-provider.ts +32 -2
- package/src/sdk/bus/slack-provider.ts +11 -0
- package/src/sdk/bus/slack-thread-binding.ts +641 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +28 -7
- package/src/sdk/bus/telegram-daemon.ts +2007 -509
- package/src/sdk/bus/telegram-reference.ts +114 -15
- package/src/sdk/bus/topic-registry.ts +674 -181
- package/src/sdk/host/host.ts +180 -1
- package/src/sdk/lifecycle-session.ts +8 -2
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session-activation.ts +127 -0
- package/src/sdk/session.ts +144 -29
- package/src/sdk/startup-capability.ts +10 -1
- package/src/session/agent-session.ts +220 -13
- package/src/session/artifacts.ts +56 -13
- package/src/session/internal/managed-session-storage.ts +228 -7
- package/src/session/session-manager.ts +225 -60
- package/src/setup/provider-presets.json +3 -3
- package/src/task/index.ts +92 -44
- package/src/thinking-metadata.ts +2 -1
- package/src/tools/ask.ts +10 -0
- package/src/tools/index.ts +15 -0
- package/src/tools/todo-write.ts +4 -4
- package/src/utils/npm-registry.ts +660 -0
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
assertManagedDirectoryRoot,
|
|
77
77
|
captureManagedFileNoFollow,
|
|
78
78
|
fsyncManagedArtifactTree,
|
|
79
|
+
MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
79
80
|
type ManagedDirectoryRoot,
|
|
80
81
|
type ManagedFileSnapshot,
|
|
81
82
|
ManagedSessionDescendantStore,
|
|
@@ -915,12 +916,25 @@ export interface ResumeTailTerminal {
|
|
|
915
916
|
|
|
916
917
|
export interface ResumeTailError {
|
|
917
918
|
kind: "error";
|
|
918
|
-
reason: "missing" | "malformed" | "unstable" | "read-failed" | "legacy_migration_disabled";
|
|
919
|
+
reason: "missing" | "malformed" | "unstable" | "read-failed" | "legacy_migration_disabled" | "oversized";
|
|
920
|
+
size?: number;
|
|
919
921
|
}
|
|
920
922
|
|
|
921
923
|
export type SessionDirectoryMigrationPolicy = ManagedMigrationPolicy;
|
|
922
924
|
export type SessionAppendPersistenceFailurePhase = "current_append" | "prior_failure";
|
|
923
925
|
|
|
926
|
+
/**
|
|
927
|
+
* Safety bound for resuming a single transcript file. Matches the managed-storage
|
|
928
|
+
* per-file artifact limit (`MANAGED_ARTIFACT_MAX_FILE_BYTES`): transcripts at or above
|
|
929
|
+
* this size are not loaded into memory wholesale because the decode + per-line JSON
|
|
930
|
+
* parse + identity hash would OOM or stall the process (#3851). The bound is a
|
|
931
|
+
* load-time guard only; it does not raise the on-disk managed-storage limit.
|
|
932
|
+
*/
|
|
933
|
+
export const RESUME_TRANSCRIPT_MAX_BYTES = MANAGED_ARTIFACT_MAX_FILE_BYTES;
|
|
934
|
+
|
|
935
|
+
export const SESSION_OVERSIZED_RECOVERY_MESSAGE =
|
|
936
|
+
"The selected session transcript is too large to resume safely. Use `gjc export <session-file>` to export its content into a new session, or remove/archive it after confirming its content is no longer needed.";
|
|
937
|
+
|
|
924
938
|
export class SessionAppendPersistenceError extends Error {
|
|
925
939
|
readonly phase: SessionAppendPersistenceFailurePhase;
|
|
926
940
|
readonly entryId: string;
|
|
@@ -964,6 +978,17 @@ export class SessionArtifactCapacityError extends Error {
|
|
|
964
978
|
}
|
|
965
979
|
}
|
|
966
980
|
|
|
981
|
+
export class SessionTranscriptOversizedError extends Error {
|
|
982
|
+
readonly code = "oversized";
|
|
983
|
+
readonly size: number;
|
|
984
|
+
|
|
985
|
+
constructor(size: number) {
|
|
986
|
+
super(SESSION_OVERSIZED_RECOVERY_MESSAGE);
|
|
987
|
+
this.name = "SessionTranscriptOversizedError";
|
|
988
|
+
this.size = size;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
967
992
|
export type ResumeTailInspection = ResumeTailResumable | ResumeTailTerminal | ResumeTailError;
|
|
968
993
|
export interface StrictSessionOpenSuccess {
|
|
969
994
|
kind: "opened";
|
|
@@ -979,6 +1004,7 @@ export interface StrictSessionOpenFailure {
|
|
|
979
1004
|
| "artifact_capacity_exceeded"
|
|
980
1005
|
| "migration_busy";
|
|
981
1006
|
message?: string;
|
|
1007
|
+
size?: number;
|
|
982
1008
|
}
|
|
983
1009
|
|
|
984
1010
|
/** Exact transcript bytes and authority captured from one descriptor-bound read. */
|
|
@@ -2346,6 +2372,12 @@ function inspectResumeSessionFile(
|
|
|
2346
2372
|
if (!before.isFile || !storage.readSnapshotSync) {
|
|
2347
2373
|
return { kind: "error", reason: "read-failed" };
|
|
2348
2374
|
}
|
|
2375
|
+
// Fail closed on oversized transcripts before the full read/decode/parse
|
|
2376
|
+
// path that would otherwise OOM or stall the process (#3851). The stat is
|
|
2377
|
+
// already in hand; this bound matches the managed-storage per-file limit.
|
|
2378
|
+
if (before.size > RESUME_TRANSCRIPT_MAX_BYTES) {
|
|
2379
|
+
return { kind: "error", reason: "oversized", size: before.size };
|
|
2380
|
+
}
|
|
2349
2381
|
|
|
2350
2382
|
let bytes: Uint8Array;
|
|
2351
2383
|
let snapshot: SessionStorageStat;
|
|
@@ -4378,12 +4410,6 @@ function extractTextFromContent(content: Message["content"]): string {
|
|
|
4378
4410
|
|
|
4379
4411
|
const SESSION_LIST_PREFIX_BYTES = 4096;
|
|
4380
4412
|
const SESSION_LIST_TRAILING_PATCH_BYTES = 4096;
|
|
4381
|
-
// Hard cap on how many trailing bytes the listers may scan for header patches.
|
|
4382
|
-
// moveTo folds cwd patches into line 1 via a full atomic rewrite, so only a
|
|
4383
|
-
// recent title patch can live unfolded near EOF; anything buried deeper is
|
|
4384
|
-
// folded on the next natural full rewrite. Keeping this bounded preserves the
|
|
4385
|
-
// O(SESSION_LIST_PREFIX_BYTES) listing cost that SESSION_LIST_PREFIX_BYTES exists for.
|
|
4386
|
-
const SESSION_LIST_TRAILING_PATCH_SCAN_CAP = SESSION_LIST_TRAILING_PATCH_BYTES * 4;
|
|
4387
4413
|
const SESSION_NAME_MAX_CHARS = 1_000;
|
|
4388
4414
|
const SESSION_LIST_PARALLEL_THRESHOLD = 64;
|
|
4389
4415
|
const SESSION_LIST_MAX_WORKERS = 16;
|
|
@@ -4419,12 +4445,17 @@ async function readSessionListTrailingPatches(
|
|
|
4419
4445
|
if (size <= SESSION_LIST_PREFIX_BYTES) return [];
|
|
4420
4446
|
const latest: HeaderPatchRecord["patch"] = {};
|
|
4421
4447
|
let position = size;
|
|
4422
|
-
|
|
4448
|
+
// Reverse-scan from EOF in fixed chunks. Stop once both cwd and title
|
|
4449
|
+
// resolve so recent patches stay cheap. Continue past the historical 16 KiB
|
|
4450
|
+
// window when a field is still missing so a buried but canonically valid
|
|
4451
|
+
// header_patch remains listable without a full sequential JSONL parse (#3633).
|
|
4452
|
+
// Chunks that cannot contain a header_patch marker skip JSON parsing.
|
|
4423
4453
|
let trailingFragment = Buffer.alloc(0);
|
|
4424
4454
|
const chunkSize = Math.min(buffer.byteLength, SESSION_LIST_TRAILING_PATCH_BYTES);
|
|
4455
|
+
const headerPatchMarker = Buffer.from("header_patch");
|
|
4425
4456
|
const handle = await fs.promises.open(file, "r");
|
|
4426
4457
|
try {
|
|
4427
|
-
while (position >
|
|
4458
|
+
while (position > 0 && (latest.cwd === undefined || latest.title === undefined)) {
|
|
4428
4459
|
const start = Math.max(0, position - chunkSize);
|
|
4429
4460
|
const length = position - start;
|
|
4430
4461
|
const { bytesRead } = await handle.read(buffer, 0, length, start);
|
|
@@ -4440,18 +4471,26 @@ async function readSessionListTrailingPatches(
|
|
|
4440
4471
|
trailingFragment = combined.subarray(0, firstNewline);
|
|
4441
4472
|
complete = combined.subarray(firstNewline + 1);
|
|
4442
4473
|
}
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4474
|
+
// Marker may straddle the discarded partial first line; still parse when
|
|
4475
|
+
// the raw chunk or carried fragment could hold a header_patch record.
|
|
4476
|
+
const mayContainPatch =
|
|
4477
|
+
complete.includes(headerPatchMarker) ||
|
|
4478
|
+
buffer.subarray(0, bytesRead).includes(headerPatchMarker) ||
|
|
4479
|
+
trailingFragment.includes(headerPatchMarker);
|
|
4480
|
+
if (mayContainPatch) {
|
|
4481
|
+
const lines = complete.toString("utf8").split("\n");
|
|
4482
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
4483
|
+
const line = lines[index];
|
|
4484
|
+
if (!line?.includes("header_patch")) continue;
|
|
4485
|
+
try {
|
|
4486
|
+
const record = JSON.parse(line) as SessionPatchRecord;
|
|
4487
|
+
if (!isHeaderPatchRecord(record)) continue;
|
|
4488
|
+
if (latest.cwd === undefined && typeof record.patch.cwd === "string") latest.cwd = record.patch.cwd;
|
|
4489
|
+
if (latest.title === undefined && typeof record.patch.title === "string")
|
|
4490
|
+
latest.title = record.patch.title;
|
|
4491
|
+
} catch {
|
|
4492
|
+
// Ignore malformed or partial records exactly as the canonical loader does.
|
|
4493
|
+
}
|
|
4455
4494
|
}
|
|
4456
4495
|
}
|
|
4457
4496
|
position = start;
|
|
@@ -4765,6 +4804,7 @@ interface SessionManagerStateSnapshot {
|
|
|
4765
4804
|
needsFullRewriteOnNextPersist: boolean;
|
|
4766
4805
|
fileEntries: FileEntry[];
|
|
4767
4806
|
materializedFileEntries: FileEntry[];
|
|
4807
|
+
adoptedArtifactManager: ArtifactManager | null;
|
|
4768
4808
|
}
|
|
4769
4809
|
|
|
4770
4810
|
/** Benchmark-derived cap for strong materialized session snapshots. */
|
|
@@ -4775,6 +4815,7 @@ export const SessionManagerTestHooks: {
|
|
|
4775
4815
|
materializedCacheMaxBytesOverride?: number;
|
|
4776
4816
|
beforeResidentTransitionIndexBuild?: () => void;
|
|
4777
4817
|
afterForkSnapshot?: () => void | Promise<void>;
|
|
4818
|
+
beforeEphemeralArtifactManagerInstall?: (dir: string) => void | Promise<void>;
|
|
4778
4819
|
} = {};
|
|
4779
4820
|
|
|
4780
4821
|
function materializedCacheMaxBytes(): number {
|
|
@@ -4841,10 +4882,13 @@ export class SessionManager {
|
|
|
4841
4882
|
// Subagents adopt the parent's manager so artifact IDs are unique across the
|
|
4842
4883
|
// whole agent tree and all files land in the parent's artifacts dir.
|
|
4843
4884
|
#adoptedArtifactManager: ArtifactManager | null = null;
|
|
4844
|
-
//
|
|
4845
|
-
//
|
|
4846
|
-
|
|
4847
|
-
#
|
|
4885
|
+
// Filesystem-backed artifact fallback for non-persistent sessions (persist=false).
|
|
4886
|
+
// The directory is created lazily on first save so artifact content is read back
|
|
4887
|
+
// from disk on demand instead of being retained in memory for the session lifetime.
|
|
4888
|
+
#ephemeralArtifactManager: ArtifactManager | null = null;
|
|
4889
|
+
#ephemeralArtifactDir: string | null = null;
|
|
4890
|
+
#ephemeralArtifactInit: Promise<ArtifactManager | null> | null = null;
|
|
4891
|
+
#ephemeralArtifactCleanups = new Set<Promise<void>>();
|
|
4848
4892
|
readonly #blobStore: BlobStore;
|
|
4849
4893
|
#residentTextBlobStore: BlobStore = new MemoryBlobStore();
|
|
4850
4894
|
#residentImageBlobStore: BlobStore;
|
|
@@ -5360,6 +5404,7 @@ export class SessionManager {
|
|
|
5360
5404
|
// Rollback snapshots must own resident data before another session reset disposes
|
|
5361
5405
|
// the ephemeral store backing the resident sentinels above.
|
|
5362
5406
|
materializedFileEntries,
|
|
5407
|
+
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
5363
5408
|
};
|
|
5364
5409
|
}
|
|
5365
5410
|
|
|
@@ -5397,7 +5442,7 @@ export class SessionManager {
|
|
|
5397
5442
|
this.#persistErrorReported = false;
|
|
5398
5443
|
this.#artifactManager = null;
|
|
5399
5444
|
this.#artifactManagerSessionFile = null;
|
|
5400
|
-
this.#adoptedArtifactManager =
|
|
5445
|
+
this.#adoptedArtifactManager = snapshot.adoptedArtifactManager;
|
|
5401
5446
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5402
5447
|
if (this.#sessionFile) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
5403
5448
|
}
|
|
@@ -5438,8 +5483,7 @@ export class SessionManager {
|
|
|
5438
5483
|
this.#flushed = false;
|
|
5439
5484
|
this.#needsFullRewriteOnNextPersist = false;
|
|
5440
5485
|
this.#ensuredOnDisk = false;
|
|
5441
|
-
|
|
5442
|
-
this.#inMemoryArtifactCounter = 0;
|
|
5486
|
+
|
|
5443
5487
|
this.#artifactManager = null;
|
|
5444
5488
|
this.#artifactManagerSessionFile = null;
|
|
5445
5489
|
this.#adoptedArtifactManager = null;
|
|
@@ -5471,6 +5515,7 @@ export class SessionManager {
|
|
|
5471
5515
|
const prepared = this.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
5472
5516
|
this.#applyFreshSessionMetadata(fresh);
|
|
5473
5517
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5518
|
+
this.#retireEphemeralArtifacts();
|
|
5474
5519
|
writeTerminalBreadcrumb(this.cwd, resolvedSessionFile);
|
|
5475
5520
|
await this.#rewriteFile();
|
|
5476
5521
|
this.#flushed = true;
|
|
@@ -5544,7 +5589,7 @@ export class SessionManager {
|
|
|
5544
5589
|
}
|
|
5545
5590
|
|
|
5546
5591
|
/** Switch to a different session file (used for resume and branching). */
|
|
5547
|
-
async setSessionFile(sessionFile: string): Promise<void> {
|
|
5592
|
+
async setSessionFile(sessionFile: string, options?: { deferEphemeralArtifactRetirement?: boolean }): Promise<void> {
|
|
5548
5593
|
this.#assertRecoveryHydrationWritable();
|
|
5549
5594
|
const resolvedSessionFile = this.storage instanceof FileSessionStorage ? path.resolve(sessionFile) : sessionFile;
|
|
5550
5595
|
const strictAdoption = this.#pendingStrictAdoption;
|
|
@@ -5641,6 +5686,7 @@ export class SessionManager {
|
|
|
5641
5686
|
prepared.dispose();
|
|
5642
5687
|
throw error;
|
|
5643
5688
|
}
|
|
5689
|
+
if (!options?.deferEphemeralArtifactRetirement) this.#retireEphemeralArtifacts();
|
|
5644
5690
|
managedTransition?.settle();
|
|
5645
5691
|
this.#pendingStrictAdoption = undefined;
|
|
5646
5692
|
this.#flushed = true;
|
|
@@ -5668,8 +5714,6 @@ export class SessionManager {
|
|
|
5668
5714
|
flushed: this.#flushed,
|
|
5669
5715
|
needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
|
|
5670
5716
|
ensuredOnDisk: this.#ensuredOnDisk,
|
|
5671
|
-
inMemoryArtifacts: this.#inMemoryArtifacts,
|
|
5672
|
-
inMemoryArtifactCounter: this.#inMemoryArtifactCounter,
|
|
5673
5717
|
artifactManager: this.#artifactManager,
|
|
5674
5718
|
artifactManagerSessionFile: this.#artifactManagerSessionFile,
|
|
5675
5719
|
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
@@ -5679,6 +5723,7 @@ export class SessionManager {
|
|
|
5679
5723
|
managedTransition?.adopt();
|
|
5680
5724
|
writeTerminalBreadcrumb(this.cwd, resolvedSessionFile);
|
|
5681
5725
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5726
|
+
if (!options?.deferEphemeralArtifactRetirement) this.#retireEphemeralArtifacts();
|
|
5682
5727
|
} catch (error) {
|
|
5683
5728
|
managedTransition?.rollback();
|
|
5684
5729
|
this.#lifecycleIdAdopted = previous.lifecycleIdAdopted;
|
|
@@ -5692,8 +5737,6 @@ export class SessionManager {
|
|
|
5692
5737
|
this.#flushed = previous.flushed;
|
|
5693
5738
|
this.#needsFullRewriteOnNextPersist = previous.needsFullRewriteOnNextPersist;
|
|
5694
5739
|
this.#ensuredOnDisk = previous.ensuredOnDisk;
|
|
5695
|
-
this.#inMemoryArtifacts = previous.inMemoryArtifacts;
|
|
5696
|
-
this.#inMemoryArtifactCounter = previous.inMemoryArtifactCounter;
|
|
5697
5740
|
this.#artifactManager = previous.artifactManager;
|
|
5698
5741
|
this.#artifactManagerSessionFile = previous.artifactManagerSessionFile;
|
|
5699
5742
|
this.#adoptedArtifactManager = previous.adoptedArtifactManager;
|
|
@@ -6139,12 +6182,12 @@ export class SessionManager {
|
|
|
6139
6182
|
this.#flushed = stage.flushed;
|
|
6140
6183
|
this.#needsFullRewriteOnNextPersist = false;
|
|
6141
6184
|
this.#ensuredOnDisk = stage.flushed;
|
|
6142
|
-
|
|
6143
|
-
this.#inMemoryArtifactCounter = 0;
|
|
6185
|
+
|
|
6144
6186
|
this.#artifactManager = null;
|
|
6145
6187
|
this.#artifactManagerSessionFile = null;
|
|
6146
6188
|
this.#adoptedArtifactManager = null;
|
|
6147
6189
|
this.#commitResidentTextStoreTransition(transition);
|
|
6190
|
+
this.#retireEphemeralArtifacts();
|
|
6148
6191
|
stage.committed = true;
|
|
6149
6192
|
this.#preparedNewSessions.delete(stage);
|
|
6150
6193
|
}
|
|
@@ -6955,6 +6998,7 @@ export class SessionManager {
|
|
|
6955
6998
|
const prepared = this.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
6956
6999
|
this.#applyFreshSessionMetadata(fresh);
|
|
6957
7000
|
this.#commitResidentTextStoreTransition(prepared);
|
|
7001
|
+
this.#retireEphemeralArtifacts();
|
|
6958
7002
|
if (writeBreadcrumb && fresh.sessionFile) writeTerminalBreadcrumb(this.cwd, fresh.sessionFile);
|
|
6959
7003
|
return fresh.sessionFile;
|
|
6960
7004
|
}
|
|
@@ -7677,6 +7721,8 @@ export class SessionManager {
|
|
|
7677
7721
|
this.#flushed = true;
|
|
7678
7722
|
}
|
|
7679
7723
|
});
|
|
7724
|
+
this.#retireEphemeralArtifacts();
|
|
7725
|
+
await this.#drainEphemeralArtifactCleanups();
|
|
7680
7726
|
this.#releaseResidentTextStore();
|
|
7681
7727
|
this.#releaseOwnedManagedAuthority();
|
|
7682
7728
|
if (this.#persistError) throw this.#persistError;
|
|
@@ -7736,6 +7782,12 @@ export class SessionManager {
|
|
|
7736
7782
|
// close leaves the session live for a genuine retry.
|
|
7737
7783
|
if (!this.#persistWriter) {
|
|
7738
7784
|
this.#releaseResidentTextStore();
|
|
7785
|
+
this.#retireEphemeralArtifacts();
|
|
7786
|
+
try {
|
|
7787
|
+
await this.#drainEphemeralArtifactCleanups();
|
|
7788
|
+
} catch (error) {
|
|
7789
|
+
outcome = { kind: "close_unknown", error: toError(error) };
|
|
7790
|
+
}
|
|
7739
7791
|
}
|
|
7740
7792
|
return outcome;
|
|
7741
7793
|
}
|
|
@@ -8024,14 +8076,40 @@ export class SessionManager {
|
|
|
8024
8076
|
this.#adoptedArtifactManager = manager;
|
|
8025
8077
|
}
|
|
8026
8078
|
|
|
8079
|
+
/** Release only the matching externally adopted manager. */
|
|
8080
|
+
releaseArtifactManager(manager: ArtifactManager): void {
|
|
8081
|
+
if (this.#adoptedArtifactManager === manager) this.#adoptedArtifactManager = null;
|
|
8082
|
+
}
|
|
8083
|
+
|
|
8084
|
+
/** Temporarily release adopted authority while an outer transition validates its successor. */
|
|
8085
|
+
stageAdoptedArtifactManagerForTransition(): void {
|
|
8086
|
+
this.#adoptedArtifactManager = null;
|
|
8087
|
+
}
|
|
8088
|
+
|
|
8089
|
+
/** Prove manager authority by exact object identity, never by pathname shape. */
|
|
8090
|
+
isArtifactManagerAuthorized(manager: ArtifactManager): boolean {
|
|
8091
|
+
return (
|
|
8092
|
+
manager === this.#adoptedArtifactManager ||
|
|
8093
|
+
manager === this.#artifactManager ||
|
|
8094
|
+
manager === this.#ephemeralArtifactManager
|
|
8095
|
+
);
|
|
8096
|
+
}
|
|
8097
|
+
|
|
8027
8098
|
/**
|
|
8028
8099
|
* Returns the ArtifactManager this session writes through. Lazily creates
|
|
8029
8100
|
* one bound to the current session file unless an external manager was
|
|
8030
|
-
* adopted via `adoptArtifactManager`.
|
|
8031
|
-
*
|
|
8101
|
+
* adopted via `adoptArtifactManager`. Falls back to the lazily created
|
|
8102
|
+
* ephemeral filesystem store once a non-persistent session has saved an
|
|
8103
|
+
* artifact, so `artifact://` stays resolvable. Returns null only when no
|
|
8104
|
+
* store has been established yet.
|
|
8032
8105
|
*/
|
|
8033
8106
|
getArtifactManager(): ArtifactManager | null {
|
|
8034
|
-
return this.#getOrCreateArtifactManager();
|
|
8107
|
+
return this.#getOrCreateArtifactManager() ?? this.#ephemeralArtifactManager;
|
|
8108
|
+
}
|
|
8109
|
+
|
|
8110
|
+
/** Linearizably establish this session's persistent or ephemeral artifact manager. */
|
|
8111
|
+
async ensureArtifactManager(): Promise<ArtifactManager | null> {
|
|
8112
|
+
return this.#getOrCreateArtifactManager() ?? (await this.#ensureEphemeralArtifactManager());
|
|
8035
8113
|
}
|
|
8036
8114
|
|
|
8037
8115
|
/**
|
|
@@ -8074,28 +8152,89 @@ export class SessionManager {
|
|
|
8074
8152
|
|
|
8075
8153
|
/**
|
|
8076
8154
|
* Save artifact content under the current session and return artifact ID.
|
|
8077
|
-
*
|
|
8155
|
+
* Persistent sessions write into the session artifact directory; non-persistent
|
|
8156
|
+
* sessions write into a lazily created temporary directory so the content is
|
|
8157
|
+
* read back from the filesystem instead of being retained in memory.
|
|
8078
8158
|
*/
|
|
8079
8159
|
async saveArtifact(content: string, toolType: string): Promise<string | undefined> {
|
|
8080
|
-
const manager = this.#getOrCreateArtifactManager();
|
|
8081
|
-
|
|
8082
|
-
// Non-persistent session: store in memory so spill truncation can proceed.
|
|
8083
|
-
if (!this.#inMemoryArtifacts) this.#inMemoryArtifacts = new Map();
|
|
8084
|
-
const id = String(this.#inMemoryArtifactCounter++);
|
|
8085
|
-
this.#inMemoryArtifacts.set(id, content);
|
|
8086
|
-
return id;
|
|
8160
|
+
const manager = this.#getOrCreateArtifactManager() ?? (await this.#ensureEphemeralArtifactManager());
|
|
8161
|
+
return manager ? manager.save(content, toolType) : undefined;
|
|
8087
8162
|
}
|
|
8088
8163
|
|
|
8089
8164
|
/**
|
|
8090
8165
|
* Resolve an artifact ID to an on-disk path for the current session.
|
|
8091
|
-
* Returns null when
|
|
8166
|
+
* Returns null when the artifact is missing.
|
|
8092
8167
|
*/
|
|
8093
8168
|
async getArtifactPath(id: string): Promise<string | null> {
|
|
8094
|
-
const manager = this
|
|
8169
|
+
const manager = this.getArtifactManager();
|
|
8095
8170
|
if (!manager) return null;
|
|
8096
8171
|
return manager.getPath(id);
|
|
8097
8172
|
}
|
|
8098
8173
|
|
|
8174
|
+
/**
|
|
8175
|
+
* Create the non-persistent session's temporary artifact directory on first use.
|
|
8176
|
+
* Returns null when the directory cannot be created; callers then report no artifact.
|
|
8177
|
+
*/
|
|
8178
|
+
async #ensureEphemeralArtifactManager(): Promise<ArtifactManager | null> {
|
|
8179
|
+
if (!this.#ephemeralArtifactInit) {
|
|
8180
|
+
let init: Promise<ArtifactManager | null>;
|
|
8181
|
+
init = fs.promises
|
|
8182
|
+
.mkdtemp(path.join(os.tmpdir(), "gjc-session-artifacts-"))
|
|
8183
|
+
.then(async dir => {
|
|
8184
|
+
try {
|
|
8185
|
+
await SessionManagerTestHooks.beforeEphemeralArtifactManagerInstall?.(dir);
|
|
8186
|
+
} catch (error) {
|
|
8187
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
8188
|
+
throw error;
|
|
8189
|
+
}
|
|
8190
|
+
const manager = new ArtifactManager(dir);
|
|
8191
|
+
if (this.#ephemeralArtifactInit !== init) {
|
|
8192
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
8193
|
+
return null;
|
|
8194
|
+
}
|
|
8195
|
+
this.#ephemeralArtifactDir = dir;
|
|
8196
|
+
this.#ephemeralArtifactManager = manager;
|
|
8197
|
+
return manager;
|
|
8198
|
+
})
|
|
8199
|
+
.catch(() => null);
|
|
8200
|
+
this.#ephemeralArtifactInit = init;
|
|
8201
|
+
}
|
|
8202
|
+
const init = this.#ephemeralArtifactInit;
|
|
8203
|
+
const manager = await init;
|
|
8204
|
+
if (!manager && this.#ephemeralArtifactInit === init) this.#ephemeralArtifactInit = null;
|
|
8205
|
+
return manager;
|
|
8206
|
+
}
|
|
8207
|
+
|
|
8208
|
+
/** Unbind the active ephemeral root only after a successor transition commits. */
|
|
8209
|
+
#retireEphemeralArtifacts(): void {
|
|
8210
|
+
const dir = this.#ephemeralArtifactDir;
|
|
8211
|
+
const init = this.#ephemeralArtifactInit;
|
|
8212
|
+
if (!dir && !init) return;
|
|
8213
|
+
this.#ephemeralArtifactManager = null;
|
|
8214
|
+
this.#ephemeralArtifactDir = null;
|
|
8215
|
+
this.#ephemeralArtifactInit = null;
|
|
8216
|
+
const cleanup = (async () => {
|
|
8217
|
+
const initialized = await init;
|
|
8218
|
+
const cleanupDir = dir ?? initialized?.dir;
|
|
8219
|
+
if (cleanupDir) await fs.promises.rm(cleanupDir, { recursive: true, force: true });
|
|
8220
|
+
})();
|
|
8221
|
+
void cleanup.catch(() => {});
|
|
8222
|
+
this.#ephemeralArtifactCleanups.add(cleanup);
|
|
8223
|
+
}
|
|
8224
|
+
|
|
8225
|
+
/** Retire predecessor ephemeral artifacts after an outer logical transition commits. */
|
|
8226
|
+
retireEphemeralArtifactsAfterTransition(): void {
|
|
8227
|
+
this.#retireEphemeralArtifacts();
|
|
8228
|
+
}
|
|
8229
|
+
|
|
8230
|
+
async #drainEphemeralArtifactCleanups(): Promise<void> {
|
|
8231
|
+
while (this.#ephemeralArtifactCleanups.size > 0) {
|
|
8232
|
+
const pending = Array.from(this.#ephemeralArtifactCleanups);
|
|
8233
|
+
this.#ephemeralArtifactCleanups.clear();
|
|
8234
|
+
await Promise.all(pending);
|
|
8235
|
+
}
|
|
8236
|
+
}
|
|
8237
|
+
|
|
8099
8238
|
/**
|
|
8100
8239
|
* Path to the unsent-input draft sidecar for an explicit session. It lives
|
|
8101
8240
|
* inside the artifacts directory so `dropSession` removes it with its owner.
|
|
@@ -8229,20 +8368,26 @@ export class SessionManager {
|
|
|
8229
8368
|
await this.#persistPatch({ type: "header_patch", patch });
|
|
8230
8369
|
}
|
|
8231
8370
|
|
|
8232
|
-
#
|
|
8371
|
+
#appendManagedRecordsSync(records: readonly (FileEntry | SessionPatchRecord)[]): void {
|
|
8233
8372
|
if (!this.#sessionFile) throw new Error("Managed transcript path is unavailable");
|
|
8234
8373
|
const store = this.#managedTranscriptStore();
|
|
8235
8374
|
const relativePath = path.basename(this.#sessionFile);
|
|
8236
|
-
|
|
8375
|
+
const bytes = Buffer.from(`${records.map(record => JSON.stringify(record)).join("\n")}\n`, "utf8");
|
|
8376
|
+
store.appendSync(relativePath, bytes);
|
|
8237
8377
|
}
|
|
8238
8378
|
|
|
8239
8379
|
async #persistPatch(record: SessionPatchRecord): Promise<void> {
|
|
8240
|
-
|
|
8380
|
+
await this.#persistPatches([record]);
|
|
8381
|
+
}
|
|
8382
|
+
|
|
8383
|
+
async #persistPatches(records: readonly SessionPatchRecord[]): Promise<void> {
|
|
8384
|
+
if (records.length === 0 || !this.persist || !this.#sessionFile || !this.storage.existsSync(this.#sessionFile))
|
|
8385
|
+
return;
|
|
8241
8386
|
await this.#queuePersistTask(async () => {
|
|
8242
8387
|
const header = this.#fileEntries.find(entry => entry.type === "session") as SessionHeader | undefined;
|
|
8243
8388
|
if (this.#needsFullRewriteOnNextPersist || !this.#flushed || (header?.version ?? 1) < CURRENT_SESSION_VERSION)
|
|
8244
8389
|
return this.#rewriteFileContents();
|
|
8245
|
-
const
|
|
8390
|
+
const persistedRecords = records.map(record =>
|
|
8246
8391
|
record.type === "entry_patch"
|
|
8247
8392
|
? (prepareEntryForPersistenceSync(
|
|
8248
8393
|
materializeResidentEntryForPersistenceSync(
|
|
@@ -8252,14 +8397,17 @@ export class SessionManager {
|
|
|
8252
8397
|
),
|
|
8253
8398
|
this.#blobStore,
|
|
8254
8399
|
) as unknown as SessionPatchRecord)
|
|
8255
|
-
: record
|
|
8400
|
+
: record,
|
|
8401
|
+
);
|
|
8256
8402
|
if (this.destination.kind === "managed") {
|
|
8257
|
-
this.#
|
|
8403
|
+
this.#appendManagedRecordsSync(persistedRecords);
|
|
8258
8404
|
return;
|
|
8259
8405
|
}
|
|
8260
8406
|
const writer = this.#ensurePersistWriter();
|
|
8261
8407
|
if (!writer) return this.#rewriteFileContents();
|
|
8262
|
-
|
|
8408
|
+
for (const persistedRecord of persistedRecords) {
|
|
8409
|
+
await writer.write(persistedRecord);
|
|
8410
|
+
}
|
|
8263
8411
|
});
|
|
8264
8412
|
}
|
|
8265
8413
|
_persist(entry: SessionEntry): void {
|
|
@@ -8308,7 +8456,7 @@ export class SessionManager {
|
|
|
8308
8456
|
new Map(),
|
|
8309
8457
|
);
|
|
8310
8458
|
const persistedEntry = prepareEntryForPersistenceSync(materializedEntry, this.#blobStore);
|
|
8311
|
-
this.#
|
|
8459
|
+
this.#appendManagedRecordsSync([persistedEntry]);
|
|
8312
8460
|
return;
|
|
8313
8461
|
}
|
|
8314
8462
|
const writer = this.#ensurePersistWriter();
|
|
@@ -9228,9 +9376,7 @@ export class SessionManager {
|
|
|
9228
9376
|
|
|
9229
9377
|
async #sanitizeLoadedOpenAIResponsesReplayMetadataAndPersist(): Promise<boolean> {
|
|
9230
9378
|
const patches = this.#sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
9231
|
-
|
|
9232
|
-
await this.#persistPatch(patch);
|
|
9233
|
-
}
|
|
9379
|
+
await this.#persistPatches(patches);
|
|
9234
9380
|
return patches.length > 0;
|
|
9235
9381
|
}
|
|
9236
9382
|
|
|
@@ -9803,6 +9949,7 @@ export class SessionManager {
|
|
|
9803
9949
|
);
|
|
9804
9950
|
manager.#applyFreshSessionMetadata(fresh);
|
|
9805
9951
|
manager.#commitResidentTextStoreTransition(transition);
|
|
9952
|
+
manager.#retireEphemeralArtifacts();
|
|
9806
9953
|
if (fresh.sessionFile) writeTerminalBreadcrumb(manager.cwd, fresh.sessionFile);
|
|
9807
9954
|
manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
9808
9955
|
await manager.#rewriteFile();
|
|
@@ -9830,6 +9977,16 @@ export class SessionManager {
|
|
|
9830
9977
|
// owner-only and reparse guards accept.
|
|
9831
9978
|
if (storage instanceof FileSessionStorage) filePath = canonicalizeTrustedPath(filePath);
|
|
9832
9979
|
if (destination.kind === "explicit" || !(storage instanceof FileSessionStorage)) {
|
|
9980
|
+
// Fail closed on oversized transcripts before the full read in the explicit path too (#3851).
|
|
9981
|
+
// A missing file falls through to loadEntriesFromFile, which returns [] and creates
|
|
9982
|
+
// a fresh session — matching the pre-existing behavior.
|
|
9983
|
+
try {
|
|
9984
|
+
const preStat = storage.statSync(filePath);
|
|
9985
|
+
if (preStat.size > RESUME_TRANSCRIPT_MAX_BYTES) throw new SessionTranscriptOversizedError(preStat.size);
|
|
9986
|
+
} catch (error) {
|
|
9987
|
+
if (error instanceof SessionTranscriptOversizedError) throw error;
|
|
9988
|
+
if (!isEnoent(error)) throw error;
|
|
9989
|
+
}
|
|
9833
9990
|
const entries = await loadEntriesFromFile(filePath, storage);
|
|
9834
9991
|
const header = entries.find(entry => entry.type === "session") as SessionHeader | undefined;
|
|
9835
9992
|
const manager = new SessionManager(
|
|
@@ -9850,6 +10007,7 @@ export class SessionManager {
|
|
|
9850
10007
|
await manager.#initSessionFile(filePath);
|
|
9851
10008
|
return manager;
|
|
9852
10009
|
}
|
|
10010
|
+
if (inspected.reason === "oversized") throw new SessionTranscriptOversizedError(inspected.size ?? 0);
|
|
9853
10011
|
throw new Error(`Could not open session: ${inspected.reason}`);
|
|
9854
10012
|
}
|
|
9855
10013
|
const opened = await SessionManager.openExistingStrict(inspected.identity, destination, storage, migrationPolicy);
|
|
@@ -9859,6 +10017,7 @@ export class SessionManager {
|
|
|
9859
10017
|
throw new SessionArtifactCapacityError(
|
|
9860
10018
|
opened.message ?? "Session artifacts exceed the migration capacity.",
|
|
9861
10019
|
);
|
|
10020
|
+
if (opened.reason === "oversized") throw new SessionTranscriptOversizedError(opened.size ?? 0);
|
|
9862
10021
|
if (opened.reason === "migration_busy") throw new SessionMigrationBusyError();
|
|
9863
10022
|
throw new Error(`Could not open session: ${opened.reason}`);
|
|
9864
10023
|
}
|
|
@@ -9905,6 +10064,7 @@ export class SessionManager {
|
|
|
9905
10064
|
const transition = manager.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
9906
10065
|
manager.#applyFreshSessionMetadata(fresh);
|
|
9907
10066
|
manager.#commitResidentTextStoreTransition(transition);
|
|
10067
|
+
manager.#retireEphemeralArtifacts();
|
|
9908
10068
|
writeTerminalBreadcrumb(manager.cwd, resolved);
|
|
9909
10069
|
await manager.#rewriteFile();
|
|
9910
10070
|
manager.#flushed = true;
|
|
@@ -10096,6 +10256,7 @@ export class SessionManager {
|
|
|
10096
10256
|
);
|
|
10097
10257
|
manager.#applyFreshSessionMetadata(fresh);
|
|
10098
10258
|
manager.#commitResidentTextStoreTransition(transition);
|
|
10259
|
+
manager.#retireEphemeralArtifacts();
|
|
10099
10260
|
manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
10100
10261
|
const beforeWrite = inspectResumeSessionFile(snapshot.sourcePath, snapshot.storage);
|
|
10101
10262
|
if ("kind" in beforeWrite) {
|
|
@@ -10595,7 +10756,10 @@ export class SessionManager {
|
|
|
10595
10756
|
const dir = destination.directory;
|
|
10596
10757
|
const openSelectedStrictly = async (selectedPath: string): Promise<SessionManager | undefined> => {
|
|
10597
10758
|
const inspected = await SessionManager.inspectSessionTailReadOnly(selectedPath, storage);
|
|
10598
|
-
if (inspected.kind === "error")
|
|
10759
|
+
if (inspected.kind === "error") {
|
|
10760
|
+
if (inspected.reason === "oversized") throw new SessionTranscriptOversizedError(inspected.size ?? 0);
|
|
10761
|
+
return undefined;
|
|
10762
|
+
}
|
|
10599
10763
|
const opened = await SessionManager.openExistingStrict(
|
|
10600
10764
|
inspected.identity,
|
|
10601
10765
|
destination,
|
|
@@ -10608,6 +10772,7 @@ export class SessionManager {
|
|
|
10608
10772
|
throw new SessionArtifactCapacityError(
|
|
10609
10773
|
opened.message ?? "Session artifacts exceed the migration capacity.",
|
|
10610
10774
|
);
|
|
10775
|
+
if (opened.reason === "oversized") throw new SessionTranscriptOversizedError(opened.size ?? 0);
|
|
10611
10776
|
if (opened.reason === "migration_busy") throw new SessionMigrationBusyError();
|
|
10612
10777
|
return undefined;
|
|
10613
10778
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[
|
|
2
|
-
{"id":"minimax","aliases":["minimax-code"],"name":"MiniMax Coding Plan","description":"OpenAI-compatible MiniMax Coding Plan endpoint","compatibility":"openai","api":"openai-completions","providerId":"minimax-code","baseUrl":"https://api.minimax.io/v1","apiKeyEnv":"MINIMAX_CODE_API_KEY","models":["
|
|
3
|
-
{"id":"minimax-cn","aliases":["minimax-code-cn","minimaxi"],"name":"MiniMax Coding Plan (China)","description":"OpenAI-compatible MiniMax China endpoint","compatibility":"openai","api":"openai-completions","providerId":"minimax-code-cn","baseUrl":"https://api.minimaxi.com/v1","apiKeyEnv":"MINIMAX_CODE_CN_API_KEY","models":["
|
|
2
|
+
{"id":"minimax","aliases":["minimax-code"],"name":"MiniMax Coding Plan","description":"OpenAI-compatible MiniMax Coding Plan endpoint","compatibility":"openai","api":"openai-completions","providerId":"minimax-code","baseUrl":"https://api.minimax.io/v1","apiKeyEnv":"MINIMAX_CODE_API_KEY","models":["MiniMax-M3"],"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"reasoningContentField":"reasoning_content"}},
|
|
3
|
+
{"id":"minimax-cn","aliases":["minimax-code-cn","minimaxi"],"name":"MiniMax Coding Plan (China)","description":"OpenAI-compatible MiniMax China endpoint","compatibility":"openai","api":"openai-completions","providerId":"minimax-code-cn","baseUrl":"https://api.minimaxi.com/v1","apiKeyEnv":"MINIMAX_CODE_CN_API_KEY","models":["MiniMax-M3"],"compat":{"supportsStore":false,"supportsDeveloperRole":false,"supportsReasoningEffort":false,"reasoningContentField":"reasoning_content"}},
|
|
4
4
|
{"id":"glm","aliases":["zai","z-ai","bigmodel"],"name":"GLM / zAI","description":"OpenAI-compatible GLM endpoint from zAI/BigModel","compatibility":"openai","api":"openai-completions","providerId":"glm-proxy","baseUrl":"https://api.z.ai/api/paas/v4","apiKeyEnv":"ZAI_API_KEY","models":["glm-4.6"],"compat":{"supportsDeveloperRole":false,"supportsReasoningEffort":false,"thinkingFormat":"zai","reasoningContentField":"reasoning_content"}},
|
|
5
|
-
{"id":"alibaba-token-plan","aliases":["alibaba","token-plan"],"name":"Alibaba Token Plan","description":"OpenAI-compatible Alibaba Token Plan endpoint","compatibility":"openai","api":"openai-completions","providerId":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","apiKeyEnv":"ALIBABA_TOKEN_PLAN_API_KEY","models":["qwen3.8-max-preview","glm-5.2","deepseek-v4-pro"],"modelApi":{"qwen3.8-max-preview":"openai-responses","glm-5.2":"openai-completions","deepseek-v4-pro":"openai-completions"},"compat":{"supportsDeveloperRole":false}}
|
|
5
|
+
{"id":"alibaba-token-plan","aliases":["alibaba","token-plan"],"name":"Alibaba Token Plan","description":"OpenAI-compatible Alibaba Token Plan endpoint","compatibility":"openai","api":"openai-completions","providerId":"alibaba-token-plan","baseUrl":"https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1","apiKeyEnv":"ALIBABA_TOKEN_PLAN_API_KEY","models":["qwen3.8-max-preview","qwen3.8-max","glm-5.2","deepseek-v4-pro"],"modelApi":{"qwen3.8-max-preview":"openai-responses","qwen3.8-max":"openai-responses","glm-5.2":"openai-completions","deepseek-v4-pro":"openai-completions"},"compat":{"supportsDeveloperRole":false}}
|
|
6
6
|
]
|