@gajae-code/coding-agent 0.12.11 → 0.12.12
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 +38 -0
- package/dist/types/cli/notify-cli.d.ts +42 -1
- package/dist/types/cli/update-cli.d.ts +9 -3
- package/dist/types/commands/notify.d.ts +6 -0
- package/dist/types/config/file-lock.d.ts +3 -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/main.d.ts +6 -2
- 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/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 +24 -7
- package/dist/types/sdk/bus/telegram-daemon.d.ts +104 -16
- package/dist/types/sdk/bus/telegram-reference.d.ts +10 -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 -0
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/session-manager.d.ts +23 -5
- package/dist/types/tools/index.d.ts +8 -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/notify-cli.ts +226 -2
- package/src/cli/update-cli.ts +90 -23
- package/src/commands/notify.ts +27 -3
- package/src/commands/sdk.ts +1 -0
- package/src/config/file-lock-gc.ts +18 -0
- package/src/config/file-lock.ts +61 -16
- package/src/config/model-profiles.ts +31 -15
- 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/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/launch-tmux.ts +53 -1
- package/src/internal-urls/docs-index.generated.ts +11 -10
- package/src/main.ts +203 -67
- package/src/modes/DESIGN.md +8 -5
- package/src/modes/components/footer.ts +10 -18
- 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/utils/host-status.ts +38 -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/index.ts +70 -0
- 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 +24 -7
- package/src/sdk/bus/telegram-daemon.ts +1961 -497
- package/src/sdk/bus/telegram-reference.ts +42 -6
- 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 +139 -28
- package/src/sdk/startup-capability.ts +10 -1
- package/src/session/agent-session.ts +220 -9
- package/src/session/artifacts.ts +56 -13
- package/src/session/internal/managed-session-storage.ts +206 -7
- package/src/session/session-manager.ts +148 -38
- package/src/setup/provider-presets.json +1 -1
- package/src/task/index.ts +92 -44
- package/src/tools/index.ts +8 -0
- package/src/utils/npm-registry.ts +660 -0
|
@@ -217,8 +217,84 @@ type ReplacementCleanupReceipt = {
|
|
|
217
217
|
predecessor: SerializedReplacementIdentity;
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
+
type LegacyReplacementCleanupIdentity = {
|
|
221
|
+
dev: bigint;
|
|
222
|
+
ino: bigint;
|
|
223
|
+
nlink: bigint;
|
|
224
|
+
size: bigint;
|
|
225
|
+
mtimeNs: bigint;
|
|
226
|
+
ctimeNs: bigint;
|
|
227
|
+
sha256: string;
|
|
228
|
+
};
|
|
229
|
+
|
|
220
230
|
const U64_MAX = 18_446_744_073_709_551_615n;
|
|
221
231
|
|
|
232
|
+
function parseCanonicalU64(value: unknown): bigint | undefined {
|
|
233
|
+
if (typeof value !== "string" || !/^(0|[1-9][0-9]*)$/.test(value) || value.length > 20) return undefined;
|
|
234
|
+
const parsed = BigInt(value);
|
|
235
|
+
return parsed <= U64_MAX ? parsed : undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function parseLegacyHexU64(value: string): bigint | undefined {
|
|
239
|
+
if (!/^[0-9a-f]+$/.test(value) || value.length > 16) return undefined;
|
|
240
|
+
const parsed = BigInt(`0x${value}`);
|
|
241
|
+
return parsed <= U64_MAX ? parsed : undefined;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
245
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
246
|
+
? (value as Record<string, unknown>)
|
|
247
|
+
: undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function legacyReplacementCleanupReceiptBinding(name: string): { dev: bigint; ino: bigint } | undefined {
|
|
251
|
+
const match = /^\.gjc-replace-cleanup-([0-9a-f]+)-([0-9a-f]+)\.json$/.exec(name);
|
|
252
|
+
if (!match?.[1] || !match[2]) return undefined;
|
|
253
|
+
const dev = parseLegacyHexU64(match[1]);
|
|
254
|
+
const ino = parseLegacyHexU64(match[2]);
|
|
255
|
+
return dev !== undefined && ino !== undefined ? { dev, ino } : undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function parseLegacyReplacementCleanupIdentity(value: unknown): LegacyReplacementCleanupIdentity | undefined {
|
|
259
|
+
const identity = asRecord(value);
|
|
260
|
+
if (!identity) return undefined;
|
|
261
|
+
const dev = parseCanonicalU64(identity.dev);
|
|
262
|
+
const ino = parseCanonicalU64(identity.ino);
|
|
263
|
+
const nlink = parseCanonicalU64(identity.nlink);
|
|
264
|
+
const size = parseCanonicalU64(identity.size);
|
|
265
|
+
const mtimeNs = parseCanonicalU64(identity.mtimeNs);
|
|
266
|
+
const ctimeNs = parseCanonicalU64(identity.ctimeNs);
|
|
267
|
+
const sha256 = identity.sha256;
|
|
268
|
+
if (
|
|
269
|
+
dev === undefined ||
|
|
270
|
+
ino === undefined ||
|
|
271
|
+
nlink === undefined ||
|
|
272
|
+
size === undefined ||
|
|
273
|
+
mtimeNs === undefined ||
|
|
274
|
+
ctimeNs === undefined ||
|
|
275
|
+
typeof sha256 !== "string" ||
|
|
276
|
+
!/^[0-9a-f]{64}$/.test(sha256)
|
|
277
|
+
)
|
|
278
|
+
return undefined;
|
|
279
|
+
return { dev, ino, nlink, size, mtimeNs, ctimeNs, sha256 };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function parseLegacyReplacementCleanupReceipt(
|
|
283
|
+
bytes: Uint8Array,
|
|
284
|
+
): { predecessor: string; successor: string; identity: LegacyReplacementCleanupIdentity } | undefined {
|
|
285
|
+
let value: unknown;
|
|
286
|
+
try {
|
|
287
|
+
value = JSON.parse(Buffer.from(bytes).toString("utf8")) as unknown;
|
|
288
|
+
} catch {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
const receipt = asRecord(value);
|
|
292
|
+
if (receipt?.version !== 1 || typeof receipt.predecessor !== "string" || typeof receipt.successor !== "string")
|
|
293
|
+
return undefined;
|
|
294
|
+
const identity = parseLegacyReplacementCleanupIdentity(receipt.identity);
|
|
295
|
+
return identity ? { predecessor: receipt.predecessor, successor: receipt.successor, identity } : undefined;
|
|
296
|
+
}
|
|
297
|
+
|
|
222
298
|
function replacementCleanupReceiptBinding(
|
|
223
299
|
name: string,
|
|
224
300
|
): { predecessor: { dev: bigint; ino: bigint }; receipt: { dev: bigint; ino: bigint } } | undefined {
|
|
@@ -258,12 +334,20 @@ function replacementReceiptPath(
|
|
|
258
334
|
}
|
|
259
335
|
|
|
260
336
|
function replacementReceiptRetirementName(
|
|
261
|
-
receipt:
|
|
262
|
-
predecessor:
|
|
337
|
+
receipt: { dev: bigint; ino: bigint },
|
|
338
|
+
predecessor: { dev: bigint; ino: bigint },
|
|
263
339
|
): string {
|
|
264
340
|
return `.gjc-receipt-remove-${receipt.dev.toString(16)}-${receipt.ino.toString(16)}-${predecessor.dev.toString(16)}-${predecessor.ino.toString(16)}`;
|
|
265
341
|
}
|
|
266
342
|
|
|
343
|
+
function replacementReceiptPlaceholderRetirementName(
|
|
344
|
+
placeholder: { dev: bigint; ino: bigint },
|
|
345
|
+
predecessor: { dev: bigint; ino: bigint },
|
|
346
|
+
receipt: { dev: bigint; ino: bigint },
|
|
347
|
+
): string {
|
|
348
|
+
return `.gjc-receipt-placeholder-remove-${placeholder.dev.toString(16)}-${placeholder.ino.toString(16)}-${predecessor.dev.toString(16)}-${predecessor.ino.toString(16)}-${receipt.dev.toString(16)}-${receipt.ino.toString(16)}`;
|
|
349
|
+
}
|
|
350
|
+
|
|
267
351
|
const ACL_FAILURE_CODES = new Set(["acl_denied", "acl_io_error", "acl_present", "acl_malformed", "acl_unknown"]);
|
|
268
352
|
const ACL_CLEAR_EVIDENCE = new Set(["cleared", "already_absent", "unsupported", "not_run"]);
|
|
269
353
|
const GENERAL_FAILURE_CODES = new Set([
|
|
@@ -785,14 +869,68 @@ export class ManagedSessionDescendantStore {
|
|
|
785
869
|
}
|
|
786
870
|
}
|
|
787
871
|
|
|
872
|
+
#recoverReplacementCleanupPlaceholder(
|
|
873
|
+
receiptPath: string,
|
|
874
|
+
binding: { predecessor: { dev: bigint; ino: bigint }; receipt: { dev: bigint; ino: bigint } },
|
|
875
|
+
placeholder: ManagedFileSnapshot,
|
|
876
|
+
): boolean {
|
|
877
|
+
if (placeholder.bytes.byteLength !== 0) return false;
|
|
878
|
+
const detachedReceiptPath = path.join(
|
|
879
|
+
this.#baseDir,
|
|
880
|
+
replacementReceiptRetirementName(binding.receipt, binding.predecessor),
|
|
881
|
+
);
|
|
882
|
+
let detachedReceipt: ManagedFileSnapshot;
|
|
883
|
+
try {
|
|
884
|
+
detachedReceipt = captureManagedFileNoFollow(detachedReceiptPath);
|
|
885
|
+
} catch (error) {
|
|
886
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
|
|
887
|
+
throw error;
|
|
888
|
+
}
|
|
889
|
+
if (detachedReceipt.identity.dev !== binding.receipt.dev || detachedReceipt.identity.ino !== binding.receipt.ino)
|
|
890
|
+
return false;
|
|
891
|
+
|
|
892
|
+
const quarantineName = replacementReceiptPlaceholderRetirementName(
|
|
893
|
+
placeholder.identity,
|
|
894
|
+
binding.predecessor,
|
|
895
|
+
binding.receipt,
|
|
896
|
+
);
|
|
897
|
+
const removed = exactUnlink(receiptPath, {
|
|
898
|
+
dev: placeholder.identity.dev,
|
|
899
|
+
ino: placeholder.identity.ino,
|
|
900
|
+
nlink: placeholder.identity.nlink,
|
|
901
|
+
parentDev: this.#subtreeRoot.dev,
|
|
902
|
+
parentIno: this.#subtreeRoot.ino,
|
|
903
|
+
size: BigInt(placeholder.identity.size),
|
|
904
|
+
mtimeNs: placeholder.identity.mtimeNs,
|
|
905
|
+
sha256: placeholder.identity.sha256,
|
|
906
|
+
quarantineName,
|
|
907
|
+
});
|
|
908
|
+
if (
|
|
909
|
+
(!exactUnlinkCompleted(removed) && removed.code !== "not_found") ||
|
|
910
|
+
removed.retainedSuccessorPath !== undefined ||
|
|
911
|
+
removed.retainedUnknownPath !== undefined
|
|
912
|
+
)
|
|
913
|
+
throw new Error(`managed_replace_receipt_cleanup_pending:${removed.code ?? "unknown"}`);
|
|
914
|
+
fsyncDirectory(this.#baseDir);
|
|
915
|
+
return true;
|
|
916
|
+
}
|
|
917
|
+
|
|
788
918
|
#detachReplacementCleanupReceipt(receiptPath: string): void {
|
|
789
919
|
const binding = replacementCleanupReceiptBinding(path.basename(receiptPath));
|
|
790
920
|
if (!binding) throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
791
|
-
|
|
792
|
-
|
|
921
|
+
let receipt: ManagedFileSnapshot;
|
|
922
|
+
try {
|
|
923
|
+
receipt = captureManagedFileNoFollow(receiptPath);
|
|
924
|
+
} catch (error) {
|
|
925
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
926
|
+
throw error;
|
|
927
|
+
}
|
|
928
|
+
if (receipt.identity.dev !== binding.receipt.dev || receipt.identity.ino !== binding.receipt.ino) {
|
|
929
|
+
if (this.#recoverReplacementCleanupPlaceholder(receiptPath, binding, receipt)) return;
|
|
793
930
|
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
931
|
+
}
|
|
794
932
|
const predecessor = binding.predecessor;
|
|
795
|
-
const quarantineName =
|
|
933
|
+
const quarantineName = replacementReceiptRetirementName(receipt.identity, predecessor);
|
|
796
934
|
const detached = exactUnlink(receiptPath, {
|
|
797
935
|
dev: receipt.identity.dev,
|
|
798
936
|
ino: receipt.identity.ino,
|
|
@@ -815,14 +953,75 @@ export class ManagedSessionDescendantStore {
|
|
|
815
953
|
fsyncDirectory(this.#baseDir);
|
|
816
954
|
}
|
|
817
955
|
|
|
956
|
+
#reconcileLegacyReplacementCleanupReceipt(receiptPath: string, name: string): void {
|
|
957
|
+
const binding = legacyReplacementCleanupReceiptBinding(name);
|
|
958
|
+
if (!binding) throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
959
|
+
const receipt = captureManagedFileNoFollow(receiptPath);
|
|
960
|
+
const parsed = parseLegacyReplacementCleanupReceipt(receipt.bytes);
|
|
961
|
+
const expectedPredecessor = path.join(
|
|
962
|
+
this.#baseDir,
|
|
963
|
+
`.gjc-exact-replace-destination-${binding.dev.toString(16)}-${binding.ino.toString(16)}`,
|
|
964
|
+
);
|
|
965
|
+
if (
|
|
966
|
+
!parsed ||
|
|
967
|
+
parsed.identity.dev !== binding.dev ||
|
|
968
|
+
parsed.identity.ino !== binding.ino ||
|
|
969
|
+
path.resolve(parsed.predecessor) !== expectedPredecessor ||
|
|
970
|
+
path.dirname(path.resolve(parsed.successor)) !== this.#baseDir
|
|
971
|
+
)
|
|
972
|
+
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
973
|
+
|
|
974
|
+
const retired = exactUnlink(expectedPredecessor, {
|
|
975
|
+
dev: parsed.identity.dev,
|
|
976
|
+
ino: parsed.identity.ino,
|
|
977
|
+
nlink: parsed.identity.nlink,
|
|
978
|
+
parentDev: this.#subtreeRoot.dev,
|
|
979
|
+
parentIno: this.#subtreeRoot.ino,
|
|
980
|
+
size: parsed.identity.size,
|
|
981
|
+
mtimeNs: parsed.identity.mtimeNs,
|
|
982
|
+
sha256: parsed.identity.sha256,
|
|
983
|
+
quarantineName: `.gjc-replace-retry-${parsed.identity.dev.toString(16)}-${parsed.identity.ino.toString(16)}`,
|
|
984
|
+
});
|
|
985
|
+
if (!exactUnlinkCompleted(retired) && retired.code !== "not_found")
|
|
986
|
+
throw new Error(`managed_replace_cleanup_pending:${retired.code ?? "unknown"}`);
|
|
987
|
+
|
|
988
|
+
const currentReceipt = captureManagedFileNoFollow(receiptPath);
|
|
989
|
+
if (
|
|
990
|
+
!sameIdentity(currentReceipt.identity, receipt.identity) ||
|
|
991
|
+
currentReceipt.identity.sha256 !== receipt.identity.sha256
|
|
992
|
+
)
|
|
993
|
+
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
994
|
+
const removed = exactUnlink(receiptPath, {
|
|
995
|
+
dev: currentReceipt.identity.dev,
|
|
996
|
+
ino: currentReceipt.identity.ino,
|
|
997
|
+
nlink: currentReceipt.identity.nlink,
|
|
998
|
+
parentDev: this.#subtreeRoot.dev,
|
|
999
|
+
parentIno: this.#subtreeRoot.ino,
|
|
1000
|
+
size: BigInt(currentReceipt.identity.size),
|
|
1001
|
+
mtimeNs: currentReceipt.identity.mtimeNs,
|
|
1002
|
+
sha256: currentReceipt.identity.sha256,
|
|
1003
|
+
quarantineName: `.gjc-receipt-remove-${currentReceipt.identity.dev.toString(16)}-${currentReceipt.identity.ino.toString(16)}`,
|
|
1004
|
+
});
|
|
1005
|
+
if (!exactUnlinkCompleted(removed) && removed.code !== "not_found")
|
|
1006
|
+
throw new Error(`managed_replace_receipt_cleanup_pending:${removed.code ?? "unknown"}`);
|
|
1007
|
+
fsyncDirectory(this.#baseDir);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
818
1010
|
#reconcileReplacementCleanupReceipts(): void {
|
|
819
1011
|
if (this.#reconcilingReplacementCleanup) return;
|
|
820
1012
|
this.#reconcilingReplacementCleanup = true;
|
|
821
1013
|
try {
|
|
822
1014
|
for (const name of fs.readdirSync(this.#baseDir)) {
|
|
823
1015
|
if (!name.startsWith(".gjc-replace-cleanup-")) continue;
|
|
824
|
-
if (
|
|
825
|
-
|
|
1016
|
+
if (replacementCleanupReceiptBinding(name)) {
|
|
1017
|
+
this.#detachReplacementCleanupReceipt(path.join(this.#baseDir, name));
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
if (legacyReplacementCleanupReceiptBinding(name)) {
|
|
1021
|
+
this.#reconcileLegacyReplacementCleanupReceipt(path.join(this.#baseDir, name), name);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
throw new Error("managed_replace_cleanup_receipt_invalid");
|
|
826
1025
|
}
|
|
827
1026
|
} finally {
|
|
828
1027
|
this.#reconcilingReplacementCleanup = false;
|
|
@@ -4765,6 +4765,7 @@ interface SessionManagerStateSnapshot {
|
|
|
4765
4765
|
needsFullRewriteOnNextPersist: boolean;
|
|
4766
4766
|
fileEntries: FileEntry[];
|
|
4767
4767
|
materializedFileEntries: FileEntry[];
|
|
4768
|
+
adoptedArtifactManager: ArtifactManager | null;
|
|
4768
4769
|
}
|
|
4769
4770
|
|
|
4770
4771
|
/** Benchmark-derived cap for strong materialized session snapshots. */
|
|
@@ -4775,6 +4776,7 @@ export const SessionManagerTestHooks: {
|
|
|
4775
4776
|
materializedCacheMaxBytesOverride?: number;
|
|
4776
4777
|
beforeResidentTransitionIndexBuild?: () => void;
|
|
4777
4778
|
afterForkSnapshot?: () => void | Promise<void>;
|
|
4779
|
+
beforeEphemeralArtifactManagerInstall?: (dir: string) => void | Promise<void>;
|
|
4778
4780
|
} = {};
|
|
4779
4781
|
|
|
4780
4782
|
function materializedCacheMaxBytes(): number {
|
|
@@ -4841,10 +4843,13 @@ export class SessionManager {
|
|
|
4841
4843
|
// Subagents adopt the parent's manager so artifact IDs are unique across the
|
|
4842
4844
|
// whole agent tree and all files land in the parent's artifacts dir.
|
|
4843
4845
|
#adoptedArtifactManager: ArtifactManager | null = null;
|
|
4844
|
-
//
|
|
4845
|
-
//
|
|
4846
|
-
|
|
4847
|
-
#
|
|
4846
|
+
// Filesystem-backed artifact fallback for non-persistent sessions (persist=false).
|
|
4847
|
+
// The directory is created lazily on first save so artifact content is read back
|
|
4848
|
+
// from disk on demand instead of being retained in memory for the session lifetime.
|
|
4849
|
+
#ephemeralArtifactManager: ArtifactManager | null = null;
|
|
4850
|
+
#ephemeralArtifactDir: string | null = null;
|
|
4851
|
+
#ephemeralArtifactInit: Promise<ArtifactManager | null> | null = null;
|
|
4852
|
+
#ephemeralArtifactCleanups = new Set<Promise<void>>();
|
|
4848
4853
|
readonly #blobStore: BlobStore;
|
|
4849
4854
|
#residentTextBlobStore: BlobStore = new MemoryBlobStore();
|
|
4850
4855
|
#residentImageBlobStore: BlobStore;
|
|
@@ -5360,6 +5365,7 @@ export class SessionManager {
|
|
|
5360
5365
|
// Rollback snapshots must own resident data before another session reset disposes
|
|
5361
5366
|
// the ephemeral store backing the resident sentinels above.
|
|
5362
5367
|
materializedFileEntries,
|
|
5368
|
+
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
5363
5369
|
};
|
|
5364
5370
|
}
|
|
5365
5371
|
|
|
@@ -5397,7 +5403,7 @@ export class SessionManager {
|
|
|
5397
5403
|
this.#persistErrorReported = false;
|
|
5398
5404
|
this.#artifactManager = null;
|
|
5399
5405
|
this.#artifactManagerSessionFile = null;
|
|
5400
|
-
this.#adoptedArtifactManager =
|
|
5406
|
+
this.#adoptedArtifactManager = snapshot.adoptedArtifactManager;
|
|
5401
5407
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5402
5408
|
if (this.#sessionFile) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
5403
5409
|
}
|
|
@@ -5438,8 +5444,7 @@ export class SessionManager {
|
|
|
5438
5444
|
this.#flushed = false;
|
|
5439
5445
|
this.#needsFullRewriteOnNextPersist = false;
|
|
5440
5446
|
this.#ensuredOnDisk = false;
|
|
5441
|
-
|
|
5442
|
-
this.#inMemoryArtifactCounter = 0;
|
|
5447
|
+
|
|
5443
5448
|
this.#artifactManager = null;
|
|
5444
5449
|
this.#artifactManagerSessionFile = null;
|
|
5445
5450
|
this.#adoptedArtifactManager = null;
|
|
@@ -5471,6 +5476,7 @@ export class SessionManager {
|
|
|
5471
5476
|
const prepared = this.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
5472
5477
|
this.#applyFreshSessionMetadata(fresh);
|
|
5473
5478
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5479
|
+
this.#retireEphemeralArtifacts();
|
|
5474
5480
|
writeTerminalBreadcrumb(this.cwd, resolvedSessionFile);
|
|
5475
5481
|
await this.#rewriteFile();
|
|
5476
5482
|
this.#flushed = true;
|
|
@@ -5544,7 +5550,7 @@ export class SessionManager {
|
|
|
5544
5550
|
}
|
|
5545
5551
|
|
|
5546
5552
|
/** Switch to a different session file (used for resume and branching). */
|
|
5547
|
-
async setSessionFile(sessionFile: string): Promise<void> {
|
|
5553
|
+
async setSessionFile(sessionFile: string, options?: { deferEphemeralArtifactRetirement?: boolean }): Promise<void> {
|
|
5548
5554
|
this.#assertRecoveryHydrationWritable();
|
|
5549
5555
|
const resolvedSessionFile = this.storage instanceof FileSessionStorage ? path.resolve(sessionFile) : sessionFile;
|
|
5550
5556
|
const strictAdoption = this.#pendingStrictAdoption;
|
|
@@ -5641,6 +5647,7 @@ export class SessionManager {
|
|
|
5641
5647
|
prepared.dispose();
|
|
5642
5648
|
throw error;
|
|
5643
5649
|
}
|
|
5650
|
+
if (!options?.deferEphemeralArtifactRetirement) this.#retireEphemeralArtifacts();
|
|
5644
5651
|
managedTransition?.settle();
|
|
5645
5652
|
this.#pendingStrictAdoption = undefined;
|
|
5646
5653
|
this.#flushed = true;
|
|
@@ -5668,8 +5675,6 @@ export class SessionManager {
|
|
|
5668
5675
|
flushed: this.#flushed,
|
|
5669
5676
|
needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
|
|
5670
5677
|
ensuredOnDisk: this.#ensuredOnDisk,
|
|
5671
|
-
inMemoryArtifacts: this.#inMemoryArtifacts,
|
|
5672
|
-
inMemoryArtifactCounter: this.#inMemoryArtifactCounter,
|
|
5673
5678
|
artifactManager: this.#artifactManager,
|
|
5674
5679
|
artifactManagerSessionFile: this.#artifactManagerSessionFile,
|
|
5675
5680
|
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
@@ -5679,6 +5684,7 @@ export class SessionManager {
|
|
|
5679
5684
|
managedTransition?.adopt();
|
|
5680
5685
|
writeTerminalBreadcrumb(this.cwd, resolvedSessionFile);
|
|
5681
5686
|
this.#commitResidentTextStoreTransition(prepared);
|
|
5687
|
+
if (!options?.deferEphemeralArtifactRetirement) this.#retireEphemeralArtifacts();
|
|
5682
5688
|
} catch (error) {
|
|
5683
5689
|
managedTransition?.rollback();
|
|
5684
5690
|
this.#lifecycleIdAdopted = previous.lifecycleIdAdopted;
|
|
@@ -5692,8 +5698,6 @@ export class SessionManager {
|
|
|
5692
5698
|
this.#flushed = previous.flushed;
|
|
5693
5699
|
this.#needsFullRewriteOnNextPersist = previous.needsFullRewriteOnNextPersist;
|
|
5694
5700
|
this.#ensuredOnDisk = previous.ensuredOnDisk;
|
|
5695
|
-
this.#inMemoryArtifacts = previous.inMemoryArtifacts;
|
|
5696
|
-
this.#inMemoryArtifactCounter = previous.inMemoryArtifactCounter;
|
|
5697
5701
|
this.#artifactManager = previous.artifactManager;
|
|
5698
5702
|
this.#artifactManagerSessionFile = previous.artifactManagerSessionFile;
|
|
5699
5703
|
this.#adoptedArtifactManager = previous.adoptedArtifactManager;
|
|
@@ -6139,12 +6143,12 @@ export class SessionManager {
|
|
|
6139
6143
|
this.#flushed = stage.flushed;
|
|
6140
6144
|
this.#needsFullRewriteOnNextPersist = false;
|
|
6141
6145
|
this.#ensuredOnDisk = stage.flushed;
|
|
6142
|
-
|
|
6143
|
-
this.#inMemoryArtifactCounter = 0;
|
|
6146
|
+
|
|
6144
6147
|
this.#artifactManager = null;
|
|
6145
6148
|
this.#artifactManagerSessionFile = null;
|
|
6146
6149
|
this.#adoptedArtifactManager = null;
|
|
6147
6150
|
this.#commitResidentTextStoreTransition(transition);
|
|
6151
|
+
this.#retireEphemeralArtifacts();
|
|
6148
6152
|
stage.committed = true;
|
|
6149
6153
|
this.#preparedNewSessions.delete(stage);
|
|
6150
6154
|
}
|
|
@@ -6955,6 +6959,7 @@ export class SessionManager {
|
|
|
6955
6959
|
const prepared = this.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
6956
6960
|
this.#applyFreshSessionMetadata(fresh);
|
|
6957
6961
|
this.#commitResidentTextStoreTransition(prepared);
|
|
6962
|
+
this.#retireEphemeralArtifacts();
|
|
6958
6963
|
if (writeBreadcrumb && fresh.sessionFile) writeTerminalBreadcrumb(this.cwd, fresh.sessionFile);
|
|
6959
6964
|
return fresh.sessionFile;
|
|
6960
6965
|
}
|
|
@@ -7677,6 +7682,8 @@ export class SessionManager {
|
|
|
7677
7682
|
this.#flushed = true;
|
|
7678
7683
|
}
|
|
7679
7684
|
});
|
|
7685
|
+
this.#retireEphemeralArtifacts();
|
|
7686
|
+
await this.#drainEphemeralArtifactCleanups();
|
|
7680
7687
|
this.#releaseResidentTextStore();
|
|
7681
7688
|
this.#releaseOwnedManagedAuthority();
|
|
7682
7689
|
if (this.#persistError) throw this.#persistError;
|
|
@@ -7736,6 +7743,12 @@ export class SessionManager {
|
|
|
7736
7743
|
// close leaves the session live for a genuine retry.
|
|
7737
7744
|
if (!this.#persistWriter) {
|
|
7738
7745
|
this.#releaseResidentTextStore();
|
|
7746
|
+
this.#retireEphemeralArtifacts();
|
|
7747
|
+
try {
|
|
7748
|
+
await this.#drainEphemeralArtifactCleanups();
|
|
7749
|
+
} catch (error) {
|
|
7750
|
+
outcome = { kind: "close_unknown", error: toError(error) };
|
|
7751
|
+
}
|
|
7739
7752
|
}
|
|
7740
7753
|
return outcome;
|
|
7741
7754
|
}
|
|
@@ -8024,14 +8037,40 @@ export class SessionManager {
|
|
|
8024
8037
|
this.#adoptedArtifactManager = manager;
|
|
8025
8038
|
}
|
|
8026
8039
|
|
|
8040
|
+
/** Release only the matching externally adopted manager. */
|
|
8041
|
+
releaseArtifactManager(manager: ArtifactManager): void {
|
|
8042
|
+
if (this.#adoptedArtifactManager === manager) this.#adoptedArtifactManager = null;
|
|
8043
|
+
}
|
|
8044
|
+
|
|
8045
|
+
/** Temporarily release adopted authority while an outer transition validates its successor. */
|
|
8046
|
+
stageAdoptedArtifactManagerForTransition(): void {
|
|
8047
|
+
this.#adoptedArtifactManager = null;
|
|
8048
|
+
}
|
|
8049
|
+
|
|
8050
|
+
/** Prove manager authority by exact object identity, never by pathname shape. */
|
|
8051
|
+
isArtifactManagerAuthorized(manager: ArtifactManager): boolean {
|
|
8052
|
+
return (
|
|
8053
|
+
manager === this.#adoptedArtifactManager ||
|
|
8054
|
+
manager === this.#artifactManager ||
|
|
8055
|
+
manager === this.#ephemeralArtifactManager
|
|
8056
|
+
);
|
|
8057
|
+
}
|
|
8058
|
+
|
|
8027
8059
|
/**
|
|
8028
8060
|
* Returns the ArtifactManager this session writes through. Lazily creates
|
|
8029
8061
|
* one bound to the current session file unless an external manager was
|
|
8030
|
-
* adopted via `adoptArtifactManager`.
|
|
8031
|
-
*
|
|
8062
|
+
* adopted via `adoptArtifactManager`. Falls back to the lazily created
|
|
8063
|
+
* ephemeral filesystem store once a non-persistent session has saved an
|
|
8064
|
+
* artifact, so `artifact://` stays resolvable. Returns null only when no
|
|
8065
|
+
* store has been established yet.
|
|
8032
8066
|
*/
|
|
8033
8067
|
getArtifactManager(): ArtifactManager | null {
|
|
8034
|
-
return this.#getOrCreateArtifactManager();
|
|
8068
|
+
return this.#getOrCreateArtifactManager() ?? this.#ephemeralArtifactManager;
|
|
8069
|
+
}
|
|
8070
|
+
|
|
8071
|
+
/** Linearizably establish this session's persistent or ephemeral artifact manager. */
|
|
8072
|
+
async ensureArtifactManager(): Promise<ArtifactManager | null> {
|
|
8073
|
+
return this.#getOrCreateArtifactManager() ?? (await this.#ensureEphemeralArtifactManager());
|
|
8035
8074
|
}
|
|
8036
8075
|
|
|
8037
8076
|
/**
|
|
@@ -8074,28 +8113,89 @@ export class SessionManager {
|
|
|
8074
8113
|
|
|
8075
8114
|
/**
|
|
8076
8115
|
* Save artifact content under the current session and return artifact ID.
|
|
8077
|
-
*
|
|
8116
|
+
* Persistent sessions write into the session artifact directory; non-persistent
|
|
8117
|
+
* sessions write into a lazily created temporary directory so the content is
|
|
8118
|
+
* read back from the filesystem instead of being retained in memory.
|
|
8078
8119
|
*/
|
|
8079
8120
|
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;
|
|
8121
|
+
const manager = this.#getOrCreateArtifactManager() ?? (await this.#ensureEphemeralArtifactManager());
|
|
8122
|
+
return manager ? manager.save(content, toolType) : undefined;
|
|
8087
8123
|
}
|
|
8088
8124
|
|
|
8089
8125
|
/**
|
|
8090
8126
|
* Resolve an artifact ID to an on-disk path for the current session.
|
|
8091
|
-
* Returns null when
|
|
8127
|
+
* Returns null when the artifact is missing.
|
|
8092
8128
|
*/
|
|
8093
8129
|
async getArtifactPath(id: string): Promise<string | null> {
|
|
8094
|
-
const manager = this
|
|
8130
|
+
const manager = this.getArtifactManager();
|
|
8095
8131
|
if (!manager) return null;
|
|
8096
8132
|
return manager.getPath(id);
|
|
8097
8133
|
}
|
|
8098
8134
|
|
|
8135
|
+
/**
|
|
8136
|
+
* Create the non-persistent session's temporary artifact directory on first use.
|
|
8137
|
+
* Returns null when the directory cannot be created; callers then report no artifact.
|
|
8138
|
+
*/
|
|
8139
|
+
async #ensureEphemeralArtifactManager(): Promise<ArtifactManager | null> {
|
|
8140
|
+
if (!this.#ephemeralArtifactInit) {
|
|
8141
|
+
let init: Promise<ArtifactManager | null>;
|
|
8142
|
+
init = fs.promises
|
|
8143
|
+
.mkdtemp(path.join(os.tmpdir(), "gjc-session-artifacts-"))
|
|
8144
|
+
.then(async dir => {
|
|
8145
|
+
try {
|
|
8146
|
+
await SessionManagerTestHooks.beforeEphemeralArtifactManagerInstall?.(dir);
|
|
8147
|
+
} catch (error) {
|
|
8148
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
8149
|
+
throw error;
|
|
8150
|
+
}
|
|
8151
|
+
const manager = new ArtifactManager(dir);
|
|
8152
|
+
if (this.#ephemeralArtifactInit !== init) {
|
|
8153
|
+
await fs.promises.rm(dir, { recursive: true, force: true });
|
|
8154
|
+
return null;
|
|
8155
|
+
}
|
|
8156
|
+
this.#ephemeralArtifactDir = dir;
|
|
8157
|
+
this.#ephemeralArtifactManager = manager;
|
|
8158
|
+
return manager;
|
|
8159
|
+
})
|
|
8160
|
+
.catch(() => null);
|
|
8161
|
+
this.#ephemeralArtifactInit = init;
|
|
8162
|
+
}
|
|
8163
|
+
const init = this.#ephemeralArtifactInit;
|
|
8164
|
+
const manager = await init;
|
|
8165
|
+
if (!manager && this.#ephemeralArtifactInit === init) this.#ephemeralArtifactInit = null;
|
|
8166
|
+
return manager;
|
|
8167
|
+
}
|
|
8168
|
+
|
|
8169
|
+
/** Unbind the active ephemeral root only after a successor transition commits. */
|
|
8170
|
+
#retireEphemeralArtifacts(): void {
|
|
8171
|
+
const dir = this.#ephemeralArtifactDir;
|
|
8172
|
+
const init = this.#ephemeralArtifactInit;
|
|
8173
|
+
if (!dir && !init) return;
|
|
8174
|
+
this.#ephemeralArtifactManager = null;
|
|
8175
|
+
this.#ephemeralArtifactDir = null;
|
|
8176
|
+
this.#ephemeralArtifactInit = null;
|
|
8177
|
+
const cleanup = (async () => {
|
|
8178
|
+
const initialized = await init;
|
|
8179
|
+
const cleanupDir = dir ?? initialized?.dir;
|
|
8180
|
+
if (cleanupDir) await fs.promises.rm(cleanupDir, { recursive: true, force: true });
|
|
8181
|
+
})();
|
|
8182
|
+
void cleanup.catch(() => {});
|
|
8183
|
+
this.#ephemeralArtifactCleanups.add(cleanup);
|
|
8184
|
+
}
|
|
8185
|
+
|
|
8186
|
+
/** Retire predecessor ephemeral artifacts after an outer logical transition commits. */
|
|
8187
|
+
retireEphemeralArtifactsAfterTransition(): void {
|
|
8188
|
+
this.#retireEphemeralArtifacts();
|
|
8189
|
+
}
|
|
8190
|
+
|
|
8191
|
+
async #drainEphemeralArtifactCleanups(): Promise<void> {
|
|
8192
|
+
while (this.#ephemeralArtifactCleanups.size > 0) {
|
|
8193
|
+
const pending = Array.from(this.#ephemeralArtifactCleanups);
|
|
8194
|
+
this.#ephemeralArtifactCleanups.clear();
|
|
8195
|
+
await Promise.all(pending);
|
|
8196
|
+
}
|
|
8197
|
+
}
|
|
8198
|
+
|
|
8099
8199
|
/**
|
|
8100
8200
|
* Path to the unsent-input draft sidecar for an explicit session. It lives
|
|
8101
8201
|
* inside the artifacts directory so `dropSession` removes it with its owner.
|
|
@@ -8229,20 +8329,26 @@ export class SessionManager {
|
|
|
8229
8329
|
await this.#persistPatch({ type: "header_patch", patch });
|
|
8230
8330
|
}
|
|
8231
8331
|
|
|
8232
|
-
#
|
|
8332
|
+
#appendManagedRecordsSync(records: readonly (FileEntry | SessionPatchRecord)[]): void {
|
|
8233
8333
|
if (!this.#sessionFile) throw new Error("Managed transcript path is unavailable");
|
|
8234
8334
|
const store = this.#managedTranscriptStore();
|
|
8235
8335
|
const relativePath = path.basename(this.#sessionFile);
|
|
8236
|
-
|
|
8336
|
+
const bytes = Buffer.from(`${records.map(record => JSON.stringify(record)).join("\n")}\n`, "utf8");
|
|
8337
|
+
store.appendSync(relativePath, bytes);
|
|
8237
8338
|
}
|
|
8238
8339
|
|
|
8239
8340
|
async #persistPatch(record: SessionPatchRecord): Promise<void> {
|
|
8240
|
-
|
|
8341
|
+
await this.#persistPatches([record]);
|
|
8342
|
+
}
|
|
8343
|
+
|
|
8344
|
+
async #persistPatches(records: readonly SessionPatchRecord[]): Promise<void> {
|
|
8345
|
+
if (records.length === 0 || !this.persist || !this.#sessionFile || !this.storage.existsSync(this.#sessionFile))
|
|
8346
|
+
return;
|
|
8241
8347
|
await this.#queuePersistTask(async () => {
|
|
8242
8348
|
const header = this.#fileEntries.find(entry => entry.type === "session") as SessionHeader | undefined;
|
|
8243
8349
|
if (this.#needsFullRewriteOnNextPersist || !this.#flushed || (header?.version ?? 1) < CURRENT_SESSION_VERSION)
|
|
8244
8350
|
return this.#rewriteFileContents();
|
|
8245
|
-
const
|
|
8351
|
+
const persistedRecords = records.map(record =>
|
|
8246
8352
|
record.type === "entry_patch"
|
|
8247
8353
|
? (prepareEntryForPersistenceSync(
|
|
8248
8354
|
materializeResidentEntryForPersistenceSync(
|
|
@@ -8252,14 +8358,17 @@ export class SessionManager {
|
|
|
8252
8358
|
),
|
|
8253
8359
|
this.#blobStore,
|
|
8254
8360
|
) as unknown as SessionPatchRecord)
|
|
8255
|
-
: record
|
|
8361
|
+
: record,
|
|
8362
|
+
);
|
|
8256
8363
|
if (this.destination.kind === "managed") {
|
|
8257
|
-
this.#
|
|
8364
|
+
this.#appendManagedRecordsSync(persistedRecords);
|
|
8258
8365
|
return;
|
|
8259
8366
|
}
|
|
8260
8367
|
const writer = this.#ensurePersistWriter();
|
|
8261
8368
|
if (!writer) return this.#rewriteFileContents();
|
|
8262
|
-
|
|
8369
|
+
for (const persistedRecord of persistedRecords) {
|
|
8370
|
+
await writer.write(persistedRecord);
|
|
8371
|
+
}
|
|
8263
8372
|
});
|
|
8264
8373
|
}
|
|
8265
8374
|
_persist(entry: SessionEntry): void {
|
|
@@ -8308,7 +8417,7 @@ export class SessionManager {
|
|
|
8308
8417
|
new Map(),
|
|
8309
8418
|
);
|
|
8310
8419
|
const persistedEntry = prepareEntryForPersistenceSync(materializedEntry, this.#blobStore);
|
|
8311
|
-
this.#
|
|
8420
|
+
this.#appendManagedRecordsSync([persistedEntry]);
|
|
8312
8421
|
return;
|
|
8313
8422
|
}
|
|
8314
8423
|
const writer = this.#ensurePersistWriter();
|
|
@@ -9228,9 +9337,7 @@ export class SessionManager {
|
|
|
9228
9337
|
|
|
9229
9338
|
async #sanitizeLoadedOpenAIResponsesReplayMetadataAndPersist(): Promise<boolean> {
|
|
9230
9339
|
const patches = this.#sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
9231
|
-
|
|
9232
|
-
await this.#persistPatch(patch);
|
|
9233
|
-
}
|
|
9340
|
+
await this.#persistPatches(patches);
|
|
9234
9341
|
return patches.length > 0;
|
|
9235
9342
|
}
|
|
9236
9343
|
|
|
@@ -9803,6 +9910,7 @@ export class SessionManager {
|
|
|
9803
9910
|
);
|
|
9804
9911
|
manager.#applyFreshSessionMetadata(fresh);
|
|
9805
9912
|
manager.#commitResidentTextStoreTransition(transition);
|
|
9913
|
+
manager.#retireEphemeralArtifacts();
|
|
9806
9914
|
if (fresh.sessionFile) writeTerminalBreadcrumb(manager.cwd, fresh.sessionFile);
|
|
9807
9915
|
manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
9808
9916
|
await manager.#rewriteFile();
|
|
@@ -9905,6 +10013,7 @@ export class SessionManager {
|
|
|
9905
10013
|
const transition = manager.#prepareFreshSessionTransition(fresh, "memory-fallback");
|
|
9906
10014
|
manager.#applyFreshSessionMetadata(fresh);
|
|
9907
10015
|
manager.#commitResidentTextStoreTransition(transition);
|
|
10016
|
+
manager.#retireEphemeralArtifacts();
|
|
9908
10017
|
writeTerminalBreadcrumb(manager.cwd, resolved);
|
|
9909
10018
|
await manager.#rewriteFile();
|
|
9910
10019
|
manager.#flushed = true;
|
|
@@ -10096,6 +10205,7 @@ export class SessionManager {
|
|
|
10096
10205
|
);
|
|
10097
10206
|
manager.#applyFreshSessionMetadata(fresh);
|
|
10098
10207
|
manager.#commitResidentTextStoreTransition(transition);
|
|
10208
|
+
manager.#retireEphemeralArtifacts();
|
|
10099
10209
|
manager.sanitizeLoadedOpenAIResponsesReplayMetadata();
|
|
10100
10210
|
const beforeWrite = inspectResumeSessionFile(snapshot.sourcePath, snapshot.storage);
|
|
10101
10211
|
if ("kind" in beforeWrite) {
|
|
@@ -2,5 +2,5 @@
|
|
|
2
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
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","qwen-3.8-max","glm-5.2","deepseek-v4-pro"],"modelApi":{"qwen3.8-max-preview":"openai-responses","qwen-3.8-max":"openai-responses","glm-5.2":"openai-completions","deepseek-v4-pro":"openai-completions"},"compat":{"supportsDeveloperRole":false}}
|
|
6
6
|
]
|