@gajae-code/coding-agent 0.12.6 → 0.12.7
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 +3 -1
- package/dist/types/async/job-manager.d.ts +36 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +8 -10
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
- package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +1 -3
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -4
- package/dist/types/sdk/host/host.d.ts +2 -23
- package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
- package/dist/types/session/artifacts.d.ts +5 -8
- package/dist/types/session/client-bridge.d.ts +0 -14
- package/dist/types/session/internal/managed-session-storage.d.ts +2 -5
- package/dist/types/session/messages.d.ts +1 -4
- package/dist/types/session/session-manager.d.ts +4 -5
- package/dist/types/session/streaming-output.d.ts +0 -32
- package/dist/types/task/receipt.d.ts +1 -2
- package/dist/types/task/types.d.ts +43 -178
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +0 -3
- package/dist/types/tools/output-meta.d.ts +5 -37
- package/dist/types/tools/subagent.d.ts +15 -0
- package/dist/types/tools/todo-write.d.ts +3 -0
- package/dist/types/tools/tool-result.d.ts +0 -1
- package/package.json +8 -8
- package/src/async/job-manager.ts +430 -46
- package/src/cursor.ts +13 -20
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +151 -425
- package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
- package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
- package/src/gjc-runtime/workflow-manifest.ts +0 -12
- package/src/internal-urls/agent-protocol.ts +38 -98
- package/src/internal-urls/artifact-protocol.ts +17 -28
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/registry-helpers.ts +0 -62
- package/src/modes/acp/acp-agent.ts +0 -24
- package/src/modes/acp/acp-client-bridge.ts +11 -154
- package/src/prompts/tools/bash.md +2 -2
- package/src/sdk/acp/adapter.ts +9 -200
- package/src/sdk/bus/chat-daemon-control.ts +3 -5
- package/src/sdk/bus/index.ts +8 -324
- package/src/sdk/bus/telegram-daemon-contract.ts +2 -4
- package/src/sdk/host/host.ts +9 -187
- package/src/sdk/host/reverse-leases.ts +42 -249
- package/src/session/agent-session.ts +15 -178
- package/src/session/artifacts.ts +74 -211
- package/src/session/client-bridge.ts +0 -15
- package/src/session/internal/managed-session-storage.ts +5 -104
- package/src/session/messages.ts +7 -32
- package/src/session/session-manager.ts +21 -12
- package/src/session/streaming-output.ts +40 -198
- package/src/task/index.ts +230 -64
- package/src/task/receipt.ts +2 -4
- package/src/task/render.ts +0 -30
- package/src/task/types.ts +10 -34
- package/src/tools/bash.ts +239 -818
- package/src/tools/eval.ts +4 -99
- package/src/tools/fetch.ts +15 -77
- package/src/tools/find.ts +3 -7
- package/src/tools/output-meta.ts +114 -696
- package/src/tools/search.ts +4 -9
- package/src/tools/subagent.ts +97 -75
- package/src/tools/todo-write.ts +47 -0
- package/src/tools/tool-result.ts +0 -1
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
- package/dist/types/utils/artifact-id.d.ts +0 -2
- package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
- package/src/utils/artifact-id.ts +0 -6
|
@@ -479,7 +479,6 @@ export class ManagedSessionDescendantStore {
|
|
|
479
479
|
/** Logical profile root inherited by nested managed session destinations. */
|
|
480
480
|
readonly #profileAgentDir: string;
|
|
481
481
|
readonly #subtreeRoot: ManagedDirectoryRoot;
|
|
482
|
-
#portableMutationFence?: ManagedStorageLock;
|
|
483
482
|
|
|
484
483
|
constructor(
|
|
485
484
|
root: ManagedDirectoryRoot,
|
|
@@ -548,40 +547,6 @@ export class ManagedSessionDescendantStore {
|
|
|
548
547
|
return this.#policy;
|
|
549
548
|
}
|
|
550
549
|
|
|
551
|
-
get hasRetainedAuthority(): boolean {
|
|
552
|
-
return this.#authority !== undefined;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
async acquireMutationFence(name: string): Promise<ManagedStorageLock | undefined> {
|
|
556
|
-
if (this.#authority) return undefined;
|
|
557
|
-
if (!/^[a-zA-Z0-9_.-]+$/.test(name)) throw new Error("invalid_mutation_fence");
|
|
558
|
-
this.#assertBound();
|
|
559
|
-
if (this.#portableMutationFence) throw new Error("migration_busy");
|
|
560
|
-
const lock = await acquireManagedLock(this.#baseDir, `.gjc-${name}`, this.#subtreeRoot, this.#policy);
|
|
561
|
-
try {
|
|
562
|
-
this.#assertBound();
|
|
563
|
-
lock.assertOwned();
|
|
564
|
-
} catch (error) {
|
|
565
|
-
await lock.release();
|
|
566
|
-
throw error;
|
|
567
|
-
}
|
|
568
|
-
this.#portableMutationFence = lock;
|
|
569
|
-
return {
|
|
570
|
-
path: lock.path,
|
|
571
|
-
attemptId: lock.attemptId,
|
|
572
|
-
assertOwned: () => {
|
|
573
|
-
this.#assertBound();
|
|
574
|
-
lock.assertOwned();
|
|
575
|
-
},
|
|
576
|
-
release: async () => {
|
|
577
|
-
try {
|
|
578
|
-
await lock.release();
|
|
579
|
-
} finally {
|
|
580
|
-
if (this.#portableMutationFence === lock) this.#portableMutationFence = undefined;
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
550
|
get profileAgentDir(): string {
|
|
586
551
|
return this.#profileAgentDir;
|
|
587
552
|
}
|
|
@@ -659,18 +624,7 @@ export class ManagedSessionDescendantStore {
|
|
|
659
624
|
this.#assertBound();
|
|
660
625
|
}
|
|
661
626
|
#assertBound(): void {
|
|
662
|
-
if (!this.#authority)
|
|
663
|
-
const named = fs.lstatSync(this.#baseDir, { bigint: true });
|
|
664
|
-
if (
|
|
665
|
-
!named.isDirectory() ||
|
|
666
|
-
named.isSymbolicLink() ||
|
|
667
|
-
named.dev !== this.#subtreeRoot.dev ||
|
|
668
|
-
named.ino !== this.#subtreeRoot.ino
|
|
669
|
-
) {
|
|
670
|
-
throw new Error("Managed descendant root binding changed");
|
|
671
|
-
}
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
627
|
+
if (!this.#authority) return;
|
|
674
628
|
const named = fs.lstatSync(this.#baseDir, { bigint: true });
|
|
675
629
|
const retained =
|
|
676
630
|
this.#authorityBaseDir === this.#baseDir
|
|
@@ -716,41 +670,24 @@ export class ManagedSessionDescendantStore {
|
|
|
716
670
|
}
|
|
717
671
|
|
|
718
672
|
async publishNoReplace(relativePath: string, bytes: Uint8Array): Promise<void> {
|
|
719
|
-
this.#assertBound();
|
|
720
|
-
this.#portableMutationFence?.assertOwned();
|
|
721
673
|
const resolved = this.#resolve(relativePath);
|
|
722
674
|
if (this.#authority) {
|
|
675
|
+
this.#assertBound();
|
|
723
676
|
this.#publishRetainedNoReplace(this.#relative(resolved), bytes);
|
|
724
677
|
this.#assertBound();
|
|
725
678
|
return;
|
|
726
679
|
}
|
|
727
|
-
await publishManagedFileNoReplace(
|
|
728
|
-
resolved,
|
|
729
|
-
bytes,
|
|
730
|
-
this.#portableMutationFence ? () => this.#portableMutationFence?.assertOwned() : undefined,
|
|
731
|
-
this.#subtreeRoot,
|
|
732
|
-
this.#policy,
|
|
733
|
-
);
|
|
734
|
-
this.#assertBound();
|
|
735
|
-
this.#portableMutationFence?.assertOwned();
|
|
680
|
+
await publishManagedFileNoReplace(resolved, bytes, undefined, this.#root, this.#policy);
|
|
736
681
|
}
|
|
737
682
|
|
|
738
683
|
publishNoReplaceSync(relativePath: string, bytes: Uint8Array): void {
|
|
739
|
-
this.#assertBound();
|
|
740
|
-
this.#portableMutationFence?.assertOwned();
|
|
741
684
|
const resolved = this.#resolve(relativePath);
|
|
742
685
|
if (!this.#authority) {
|
|
743
|
-
publishManagedFileNoReplaceSync(
|
|
744
|
-
resolved,
|
|
745
|
-
bytes,
|
|
746
|
-
this.#subtreeRoot,
|
|
747
|
-
this.#policy,
|
|
748
|
-
this.#portableMutationFence ? () => this.#portableMutationFence?.assertOwned() : undefined,
|
|
749
|
-
);
|
|
686
|
+
publishManagedFileNoReplaceSync(resolved, bytes, this.#root, this.#policy);
|
|
750
687
|
this.#assertBound();
|
|
751
|
-
this.#portableMutationFence?.assertOwned();
|
|
752
688
|
return;
|
|
753
689
|
}
|
|
690
|
+
this.#assertBound();
|
|
754
691
|
this.#publishRetainedNoReplace(this.#relative(resolved), bytes);
|
|
755
692
|
this.#assertBound();
|
|
756
693
|
}
|
|
@@ -768,34 +705,6 @@ export class ManagedSessionDescendantStore {
|
|
|
768
705
|
this.#assertBound();
|
|
769
706
|
}
|
|
770
707
|
|
|
771
|
-
replaceExpected(relativePath: string, bytes: Uint8Array, expected: ManagedFileSnapshot): void {
|
|
772
|
-
this.#assertBound();
|
|
773
|
-
const resolved = this.#resolve(relativePath);
|
|
774
|
-
if (!this.#authority) {
|
|
775
|
-
const fence = this.#portableMutationFence;
|
|
776
|
-
if (!fence) throw new Error("managed_replace_exact_unavailable");
|
|
777
|
-
fence.assertOwned();
|
|
778
|
-
const current = captureManagedFileNoFollow(resolved);
|
|
779
|
-
if (!sameIdentity(current.identity, expected.identity) || current.identity.sha256 !== expected.identity.sha256)
|
|
780
|
-
throw new Error("managed_replace_identity_mismatch");
|
|
781
|
-
replaceManagedFileSync(resolved, bytes, this.#subtreeRoot, this.#policy, () => fence.assertOwned());
|
|
782
|
-
fence.assertOwned();
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
const replaced = (this.#authority as RecoveryFsRoot & RetainedManagedReplacer).replaceManaged(
|
|
786
|
-
this.#relative(resolved),
|
|
787
|
-
bytes,
|
|
788
|
-
expected.identity.dev.toString(),
|
|
789
|
-
expected.identity.ino.toString(),
|
|
790
|
-
String(expected.identity.size),
|
|
791
|
-
expected.identity.mtimeNs.toString(),
|
|
792
|
-
expected.identity.ctimeNs.toString(),
|
|
793
|
-
expected.identity.sha256,
|
|
794
|
-
);
|
|
795
|
-
if (!replaced.ok) throw new Error(replaced.code ?? "managed_replace_failed");
|
|
796
|
-
this.#assertBound();
|
|
797
|
-
}
|
|
798
|
-
|
|
799
708
|
replaceSync(relativePath: string, bytes: Uint8Array): void {
|
|
800
709
|
this.#assertBound();
|
|
801
710
|
const resolved = this.#resolve(relativePath);
|
|
@@ -1525,7 +1434,6 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1525
1434
|
bytes: Uint8Array,
|
|
1526
1435
|
root?: ManagedDirectoryRoot,
|
|
1527
1436
|
policy: ManagedSessionSecurityPolicy = "default",
|
|
1528
|
-
assertOwned?: () => void,
|
|
1529
1437
|
): void {
|
|
1530
1438
|
const parent = path.dirname(destination);
|
|
1531
1439
|
ensureManagedDirectory(parent, root, policy);
|
|
@@ -1536,7 +1444,6 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1536
1444
|
let outcome: NativePublishOutcome | undefined;
|
|
1537
1445
|
|
|
1538
1446
|
try {
|
|
1539
|
-
assertOwned?.();
|
|
1540
1447
|
fd = fs.openSync(
|
|
1541
1448
|
staging,
|
|
1542
1449
|
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
|
|
@@ -1550,9 +1457,7 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1550
1457
|
const staged = fs.fstatSync(fd, { bigint: true });
|
|
1551
1458
|
stagingIdentity = { dev: staged.dev, ino: staged.ino };
|
|
1552
1459
|
|
|
1553
|
-
assertOwned?.();
|
|
1554
1460
|
outcome = classifyNativePublishOutcome(renameNoReplacePath(staging, destination));
|
|
1555
|
-
assertOwned?.();
|
|
1556
1461
|
if (!outcome.ok) throw publishFailure(outcome);
|
|
1557
1462
|
|
|
1558
1463
|
const named = fs.lstatSync(destination, { bigint: true });
|
|
@@ -1588,7 +1493,6 @@ export function replaceManagedFileSync(
|
|
|
1588
1493
|
bytes: Uint8Array,
|
|
1589
1494
|
root: ManagedDirectoryRoot,
|
|
1590
1495
|
policy: ManagedSessionSecurityPolicy = "default",
|
|
1591
|
-
assertFence?: () => void,
|
|
1592
1496
|
): void {
|
|
1593
1497
|
const parent = path.dirname(destination);
|
|
1594
1498
|
ensureManagedDirectory(parent, root, policy);
|
|
@@ -1596,7 +1500,6 @@ export function replaceManagedFileSync(
|
|
|
1596
1500
|
let fd: number | undefined;
|
|
1597
1501
|
let stagedIdentity: { dev: bigint; ino: bigint } | undefined;
|
|
1598
1502
|
try {
|
|
1599
|
-
assertFence?.();
|
|
1600
1503
|
fd = fs.openSync(
|
|
1601
1504
|
staging,
|
|
1602
1505
|
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
|
|
@@ -1610,9 +1513,7 @@ export function replaceManagedFileSync(
|
|
|
1610
1513
|
const staged = fs.fstatSync(fd, { bigint: true });
|
|
1611
1514
|
stagedIdentity = { dev: staged.dev, ino: staged.ino };
|
|
1612
1515
|
assertManagedDirectoryRoot(root);
|
|
1613
|
-
assertFence?.();
|
|
1614
1516
|
fs.renameSync(staging, destination);
|
|
1615
|
-
assertFence?.();
|
|
1616
1517
|
assertManagedDirectoryRoot(root);
|
|
1617
1518
|
const named = fs.lstatSync(destination, { bigint: true });
|
|
1618
1519
|
if (!named.isFile() || named.isSymbolicLink() || named.dev !== staged.dev || named.ino !== staged.ino) {
|
package/src/session/messages.ts
CHANGED
|
@@ -194,39 +194,14 @@ function utf8Suffix(text: string, maxBytes: number): string {
|
|
|
194
194
|
* next provider context is constructed. Byte boundaries never split UTF-8
|
|
195
195
|
* code points, so head/tail recovery remains readable for emoji and CJK text.
|
|
196
196
|
*/
|
|
197
|
-
export function createPreAdmissionArtifactSpillPreview(
|
|
198
|
-
fullText: string,
|
|
199
|
-
artifactId: string,
|
|
200
|
-
digest: string,
|
|
201
|
-
options: { maxInlineBytes?: number; fullOutput?: boolean } = {},
|
|
202
|
-
): string {
|
|
197
|
+
export function createPreAdmissionArtifactSpillPreview(fullText: string, artifactId: string, digest: string): string {
|
|
203
198
|
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
`\n\n[${totalBytes} bytes omitted; sha256:${digest}; ${referenceKind}: artifact://${artifactId}]\n\n`,
|
|
211
|
-
"utf-8",
|
|
212
|
-
);
|
|
213
|
-
const contentBudget = Math.max(0, maxInlineBytes - receiptReserve);
|
|
214
|
-
headBudget = Math.min(headBudget, Math.ceil(contentBudget / 2));
|
|
215
|
-
tailBudget = Math.min(tailBudget, Math.floor(contentBudget / 2));
|
|
216
|
-
}
|
|
217
|
-
for (;;) {
|
|
218
|
-
const head = utf8Prefix(fullText, headBudget);
|
|
219
|
-
const tail = utf8Suffix(fullText, tailBudget);
|
|
220
|
-
const retainedBytes = Buffer.byteLength(head, "utf-8") + Buffer.byteLength(tail, "utf-8");
|
|
221
|
-
const omittedBytes = Math.max(0, totalBytes - retainedBytes);
|
|
222
|
-
const receipt = `[${omittedBytes} bytes omitted; sha256:${digest}; ${referenceKind}: artifact://${artifactId}]`;
|
|
223
|
-
const preview = `${head}\n\n${receipt}\n\n${tail}`;
|
|
224
|
-
if (maxInlineBytes === undefined || Buffer.byteLength(preview, "utf-8") <= maxInlineBytes) return preview;
|
|
225
|
-
if (headBudget === 0 && tailBudget === 0) return utf8Prefix(receipt, maxInlineBytes);
|
|
226
|
-
const overflow = Buffer.byteLength(preview, "utf-8") - maxInlineBytes;
|
|
227
|
-
if (tailBudget >= headBudget) tailBudget = Math.max(0, tailBudget - overflow);
|
|
228
|
-
else headBudget = Math.max(0, headBudget - overflow);
|
|
229
|
-
}
|
|
199
|
+
const head = utf8Prefix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES);
|
|
200
|
+
const tail = utf8Suffix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES);
|
|
201
|
+
const retainedBytes = Buffer.byteLength(head, "utf-8") + Buffer.byteLength(tail, "utf-8");
|
|
202
|
+
const omittedBytes = Math.max(0, totalBytes - retainedBytes);
|
|
203
|
+
const receipt = `[${omittedBytes} bytes omitted; sha256:${digest}; full output: artifact://${artifactId}]`;
|
|
204
|
+
return `${head}\n\n${receipt}\n\n${tail}`;
|
|
230
205
|
}
|
|
231
206
|
|
|
232
207
|
/**
|
|
@@ -37,7 +37,7 @@ import { assertSafePathComponent } from "../gjc-runtime/session-layout";
|
|
|
37
37
|
import { writeTextAtomic } from "../gjc-runtime/state-writer";
|
|
38
38
|
import type { ManagedLegacyLocalMigrationSource } from "../internal-urls/local-protocol";
|
|
39
39
|
import * as git from "../utils/git";
|
|
40
|
-
import { ArtifactManager
|
|
40
|
+
import { ArtifactManager } from "./artifacts";
|
|
41
41
|
import {
|
|
42
42
|
type BlobPutResult,
|
|
43
43
|
BlobStore,
|
|
@@ -1133,7 +1133,7 @@ export function createReadonlySessionManager(manager: SessionManager): ReadonlyS
|
|
|
1133
1133
|
|
|
1134
1134
|
/** Internal artifact-writing capability. Read-only facades expose it only through a private weak-map lookup. */
|
|
1135
1135
|
export type SessionArtifactCapability = Readonly<
|
|
1136
|
-
Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "
|
|
1136
|
+
Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "putBlob">
|
|
1137
1137
|
>;
|
|
1138
1138
|
|
|
1139
1139
|
const sessionArtifactCapabilities = new WeakMap<SessionManager, SessionArtifactCapability>();
|
|
@@ -1150,7 +1150,6 @@ export function sessionArtifactCapability(value: unknown): SessionArtifactCapabi
|
|
|
1150
1150
|
capability = Object.freeze({
|
|
1151
1151
|
allocateArtifactPath: value.allocateArtifactPath.bind(value),
|
|
1152
1152
|
saveArtifact: value.saveArtifact.bind(value),
|
|
1153
|
-
getArtifactPath: value.getArtifactPath.bind(value),
|
|
1154
1153
|
putBlob: value.putBlob.bind(value),
|
|
1155
1154
|
});
|
|
1156
1155
|
sessionArtifactCapabilities.set(value, capability);
|
|
@@ -4772,6 +4771,10 @@ export class SessionManager {
|
|
|
4772
4771
|
// Subagents adopt the parent's manager so artifact IDs are unique across the
|
|
4773
4772
|
// whole agent tree and all files land in the parent's artifacts dir.
|
|
4774
4773
|
#adoptedArtifactManager: ArtifactManager | null = null;
|
|
4774
|
+
// In-memory artifact fallback for non-persistent sessions (persist=false).
|
|
4775
|
+
// Keyed by sequential numeric ID string; mirrors the file-based ArtifactManager ID scheme.
|
|
4776
|
+
#inMemoryArtifacts: Map<string, string> | null = null;
|
|
4777
|
+
#inMemoryArtifactCounter = 0;
|
|
4775
4778
|
readonly #blobStore: BlobStore;
|
|
4776
4779
|
#residentTextBlobStore: BlobStore = new MemoryBlobStore();
|
|
4777
4780
|
#residentImageBlobStore: BlobStore;
|
|
@@ -5365,6 +5368,8 @@ export class SessionManager {
|
|
|
5365
5368
|
this.#flushed = false;
|
|
5366
5369
|
this.#needsFullRewriteOnNextPersist = false;
|
|
5367
5370
|
this.#ensuredOnDisk = false;
|
|
5371
|
+
this.#inMemoryArtifacts = null;
|
|
5372
|
+
this.#inMemoryArtifactCounter = 0;
|
|
5368
5373
|
this.#artifactManager = null;
|
|
5369
5374
|
this.#artifactManagerSessionFile = null;
|
|
5370
5375
|
this.#adoptedArtifactManager = null;
|
|
@@ -5583,6 +5588,8 @@ export class SessionManager {
|
|
|
5583
5588
|
flushed: this.#flushed,
|
|
5584
5589
|
needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
|
|
5585
5590
|
ensuredOnDisk: this.#ensuredOnDisk,
|
|
5591
|
+
inMemoryArtifacts: this.#inMemoryArtifacts,
|
|
5592
|
+
inMemoryArtifactCounter: this.#inMemoryArtifactCounter,
|
|
5586
5593
|
artifactManager: this.#artifactManager,
|
|
5587
5594
|
artifactManagerSessionFile: this.#artifactManagerSessionFile,
|
|
5588
5595
|
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
@@ -5605,6 +5612,8 @@ export class SessionManager {
|
|
|
5605
5612
|
this.#flushed = previous.flushed;
|
|
5606
5613
|
this.#needsFullRewriteOnNextPersist = previous.needsFullRewriteOnNextPersist;
|
|
5607
5614
|
this.#ensuredOnDisk = previous.ensuredOnDisk;
|
|
5615
|
+
this.#inMemoryArtifacts = previous.inMemoryArtifacts;
|
|
5616
|
+
this.#inMemoryArtifactCounter = previous.inMemoryArtifactCounter;
|
|
5608
5617
|
this.#artifactManager = previous.artifactManager;
|
|
5609
5618
|
this.#artifactManagerSessionFile = previous.artifactManagerSessionFile;
|
|
5610
5619
|
this.#adoptedArtifactManager = previous.adoptedArtifactManager;
|
|
@@ -6050,6 +6059,8 @@ export class SessionManager {
|
|
|
6050
6059
|
this.#flushed = stage.flushed;
|
|
6051
6060
|
this.#needsFullRewriteOnNextPersist = false;
|
|
6052
6061
|
this.#ensuredOnDisk = stage.flushed;
|
|
6062
|
+
this.#inMemoryArtifacts = null;
|
|
6063
|
+
this.#inMemoryArtifactCounter = 0;
|
|
6053
6064
|
this.#artifactManager = null;
|
|
6054
6065
|
this.#artifactManagerSessionFile = null;
|
|
6055
6066
|
this.#adoptedArtifactManager = null;
|
|
@@ -7979,19 +7990,17 @@ export class SessionManager {
|
|
|
7979
7990
|
}
|
|
7980
7991
|
|
|
7981
7992
|
/**
|
|
7982
|
-
* Save artifact content under the current
|
|
7983
|
-
*
|
|
7993
|
+
* Save artifact content under the current session and return artifact ID.
|
|
7994
|
+
* Returns an artifact ID for all sessions (file-backed for persistent, in-memory fallback otherwise).
|
|
7984
7995
|
*/
|
|
7985
7996
|
async saveArtifact(content: string, toolType: string): Promise<string | undefined> {
|
|
7986
7997
|
const manager = this.#getOrCreateArtifactManager();
|
|
7987
7998
|
if (manager) return manager.save(content, toolType);
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
|
|
7992
|
-
|
|
7993
|
-
if (!manager) return undefined;
|
|
7994
|
-
return manager.saveWithReceipt(content, toolType);
|
|
7999
|
+
// Non-persistent session: store in memory so spill truncation can proceed.
|
|
8000
|
+
if (!this.#inMemoryArtifacts) this.#inMemoryArtifacts = new Map();
|
|
8001
|
+
const id = String(this.#inMemoryArtifactCounter++);
|
|
8002
|
+
this.#inMemoryArtifacts.set(id, content);
|
|
8003
|
+
return id;
|
|
7995
8004
|
}
|
|
7996
8005
|
|
|
7997
8006
|
/**
|