@gajae-code/coding-agent 0.12.5 → 0.12.6
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 +32 -0
- package/README.md +4 -1
- package/dist/types/eval/backend.d.ts +2 -2
- package/dist/types/exec/bash-executor.d.ts +10 -8
- package/dist/types/extensibility/extensions/runner.d.ts +9 -7
- package/dist/types/gjc-runtime/launch-tmux.d.ts +22 -3
- package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +3 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +14 -2
- package/dist/types/gjc-runtime/team-store.d.ts +0 -4
- package/dist/types/gjc-runtime/team-workers.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-provider-context.d.ts +57 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +8 -1
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +3 -0
- package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +83 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +57 -4
- package/dist/types/internal-urls/registry-helpers.d.ts +18 -0
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +1 -0
- package/dist/types/modes/components/plugin-settings.d.ts +2 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +18 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +13 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -3
- package/dist/types/sdk/bus/index.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -2
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -3
- package/dist/types/sdk/bus/topic-registry.d.ts +63 -3
- package/dist/types/sdk/host/host.d.ts +23 -2
- package/dist/types/sdk/host/reverse-leases.d.ts +7 -4
- package/dist/types/session/agent-session.d.ts +5 -4
- package/dist/types/session/artifacts.d.ts +8 -5
- package/dist/types/session/attempt-record-store.d.ts +19 -0
- package/dist/types/session/client-bridge.d.ts +14 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +5 -2
- package/dist/types/session/messages.d.ts +4 -1
- package/dist/types/session/session-manager.d.ts +6 -4
- package/dist/types/session/session-storage.d.ts +8 -0
- package/dist/types/session/streaming-output.d.ts +32 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +178 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/fetch.d.ts +3 -0
- package/dist/types/tools/output-meta.d.ts +37 -5
- package/dist/types/tools/tool-result.d.ts +1 -0
- package/dist/types/utils/artifact-id.d.ts +2 -0
- package/package.json +8 -8
- package/scripts/capture-sticky-viewport-showcase.ts +131 -5
- package/scripts/verify-gjc-sdk-canonicalization.ts +128 -183
- package/scripts/verify-sticky-viewport-showcase.ts +620 -93
- package/src/config/model-profiles.ts +8 -0
- package/src/config/settings-schema.ts +17 -3
- package/src/cursor.ts +20 -13
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +5 -2
- package/src/eval/backend.ts +2 -2
- package/src/exec/bash-executor.ts +425 -151
- package/src/extensibility/extensions/runner.ts +80 -6
- package/src/extensibility/extensions/wrapper.ts +22 -15
- package/src/extensibility/runtime-skill-discovery.ts +4 -6
- package/src/gjc-runtime/launch-tmux.ts +309 -293
- package/src/gjc-runtime/managed-owner-supervisor.ts +6 -2
- package/src/gjc-runtime/psmux-detect.ts +52 -6
- package/src/gjc-runtime/team-launch.ts +50 -2
- package/src/gjc-runtime/team-runtime.ts +716 -251
- package/src/gjc-runtime/team-store.ts +25 -7
- package/src/gjc-runtime/team-workers.ts +161 -1
- package/src/gjc-runtime/tmux-common.ts +16 -4
- package/src/gjc-runtime/tmux-gc.ts +37 -5
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -0
- package/src/gjc-runtime/tmux-provider-context.ts +451 -0
- package/src/gjc-runtime/tmux-sessions.ts +409 -156
- package/src/gjc-runtime/ultragoal-change-set.ts +90 -44
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +7 -0
- package/src/gjc-runtime/ultragoal-review-source.ts +378 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +640 -159
- package/src/gjc-runtime/workflow-manifest.generated.json +40 -1
- package/src/gjc-runtime/workflow-manifest.ts +12 -0
- package/src/internal-urls/agent-protocol.ts +98 -38
- package/src/internal-urls/artifact-protocol.ts +28 -17
- package/src/internal-urls/docs-index.generated.ts +5 -5
- package/src/internal-urls/local-protocol.ts +39 -18
- package/src/internal-urls/registry-helpers.ts +62 -0
- package/src/modes/acp/acp-agent.ts +51 -13
- package/src/modes/acp/acp-client-bridge.ts +154 -11
- package/src/modes/components/gjc-bundle-settings.ts +2 -0
- package/src/modes/components/plugin-settings.ts +5 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/monitor.md +4 -4
- package/src/sdk/acp/adapter.ts +200 -9
- package/src/sdk/broker/broker.ts +93 -10
- package/src/sdk/broker/lifecycle-ledger.ts +227 -11
- package/src/sdk/broker/lifecycle.ts +691 -92
- package/src/sdk/bus/chat-daemon-control.ts +32 -6
- package/src/sdk/bus/index.ts +347 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -2
- package/src/sdk/bus/telegram-daemon.ts +283 -44
- package/src/sdk/bus/topic-registry.ts +133 -10
- package/src/sdk/host/host.ts +187 -9
- package/src/sdk/host/reverse-leases.ts +249 -42
- package/src/sdk/session.ts +7 -6
- package/src/session/agent-session.ts +930 -354
- package/src/session/artifacts.ts +211 -74
- package/src/session/attempt-record-store.ts +106 -0
- package/src/session/client-bridge.ts +15 -0
- package/src/session/internal/managed-session-scope.ts +252 -67
- package/src/session/internal/managed-session-storage.ts +105 -5
- package/src/session/messages.ts +32 -7
- package/src/session/session-manager.ts +19 -21
- package/src/session/session-storage.ts +146 -24
- package/src/session/streaming-output.ts +198 -40
- package/src/task/index.ts +45 -0
- package/src/task/provider-retry-status.ts +4 -1
- package/src/task/receipt.ts +4 -0
- package/src/task/render.ts +30 -0
- package/src/task/types.ts +34 -0
- package/src/tools/bash.ts +818 -239
- package/src/tools/eval.ts +99 -4
- package/src/tools/fetch.ts +77 -15
- package/src/tools/find.ts +7 -3
- package/src/tools/monitor.ts +7 -1
- package/src/tools/output-meta.ts +706 -116
- package/src/tools/search.ts +9 -4
- package/src/tools/tool-result.ts +1 -0
- package/src/utils/artifact-id.ts +6 -0
|
@@ -89,6 +89,7 @@ function sameDirectoryTreeSnapshotAfterRename(
|
|
|
89
89
|
entry.kind === other.kind &&
|
|
90
90
|
entry.dev === other.dev &&
|
|
91
91
|
entry.ino === other.ino &&
|
|
92
|
+
entry.nlink === other.nlink &&
|
|
92
93
|
entry.size === other.size &&
|
|
93
94
|
entry.mtimeNs === other.mtimeNs &&
|
|
94
95
|
(entry.relativePath === "" || entry.ctimeNs === other.ctimeNs) &&
|
|
@@ -478,6 +479,7 @@ export class ManagedSessionDescendantStore {
|
|
|
478
479
|
/** Logical profile root inherited by nested managed session destinations. */
|
|
479
480
|
readonly #profileAgentDir: string;
|
|
480
481
|
readonly #subtreeRoot: ManagedDirectoryRoot;
|
|
482
|
+
#portableMutationFence?: ManagedStorageLock;
|
|
481
483
|
|
|
482
484
|
constructor(
|
|
483
485
|
root: ManagedDirectoryRoot,
|
|
@@ -546,6 +548,40 @@ export class ManagedSessionDescendantStore {
|
|
|
546
548
|
return this.#policy;
|
|
547
549
|
}
|
|
548
550
|
|
|
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
|
+
}
|
|
549
585
|
get profileAgentDir(): string {
|
|
550
586
|
return this.#profileAgentDir;
|
|
551
587
|
}
|
|
@@ -623,7 +659,18 @@ export class ManagedSessionDescendantStore {
|
|
|
623
659
|
this.#assertBound();
|
|
624
660
|
}
|
|
625
661
|
#assertBound(): void {
|
|
626
|
-
if (!this.#authority)
|
|
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
674
|
const named = fs.lstatSync(this.#baseDir, { bigint: true });
|
|
628
675
|
const retained =
|
|
629
676
|
this.#authorityBaseDir === this.#baseDir
|
|
@@ -669,24 +716,41 @@ export class ManagedSessionDescendantStore {
|
|
|
669
716
|
}
|
|
670
717
|
|
|
671
718
|
async publishNoReplace(relativePath: string, bytes: Uint8Array): Promise<void> {
|
|
719
|
+
this.#assertBound();
|
|
720
|
+
this.#portableMutationFence?.assertOwned();
|
|
672
721
|
const resolved = this.#resolve(relativePath);
|
|
673
722
|
if (this.#authority) {
|
|
674
|
-
this.#assertBound();
|
|
675
723
|
this.#publishRetainedNoReplace(this.#relative(resolved), bytes);
|
|
676
724
|
this.#assertBound();
|
|
677
725
|
return;
|
|
678
726
|
}
|
|
679
|
-
await publishManagedFileNoReplace(
|
|
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
736
|
}
|
|
681
737
|
|
|
682
738
|
publishNoReplaceSync(relativePath: string, bytes: Uint8Array): void {
|
|
739
|
+
this.#assertBound();
|
|
740
|
+
this.#portableMutationFence?.assertOwned();
|
|
683
741
|
const resolved = this.#resolve(relativePath);
|
|
684
742
|
if (!this.#authority) {
|
|
685
|
-
publishManagedFileNoReplaceSync(
|
|
743
|
+
publishManagedFileNoReplaceSync(
|
|
744
|
+
resolved,
|
|
745
|
+
bytes,
|
|
746
|
+
this.#subtreeRoot,
|
|
747
|
+
this.#policy,
|
|
748
|
+
this.#portableMutationFence ? () => this.#portableMutationFence?.assertOwned() : undefined,
|
|
749
|
+
);
|
|
686
750
|
this.#assertBound();
|
|
751
|
+
this.#portableMutationFence?.assertOwned();
|
|
687
752
|
return;
|
|
688
753
|
}
|
|
689
|
-
this.#assertBound();
|
|
690
754
|
this.#publishRetainedNoReplace(this.#relative(resolved), bytes);
|
|
691
755
|
this.#assertBound();
|
|
692
756
|
}
|
|
@@ -704,6 +768,34 @@ export class ManagedSessionDescendantStore {
|
|
|
704
768
|
this.#assertBound();
|
|
705
769
|
}
|
|
706
770
|
|
|
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
|
+
|
|
707
799
|
replaceSync(relativePath: string, bytes: Uint8Array): void {
|
|
708
800
|
this.#assertBound();
|
|
709
801
|
const resolved = this.#resolve(relativePath);
|
|
@@ -1433,6 +1525,7 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1433
1525
|
bytes: Uint8Array,
|
|
1434
1526
|
root?: ManagedDirectoryRoot,
|
|
1435
1527
|
policy: ManagedSessionSecurityPolicy = "default",
|
|
1528
|
+
assertOwned?: () => void,
|
|
1436
1529
|
): void {
|
|
1437
1530
|
const parent = path.dirname(destination);
|
|
1438
1531
|
ensureManagedDirectory(parent, root, policy);
|
|
@@ -1443,6 +1536,7 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1443
1536
|
let outcome: NativePublishOutcome | undefined;
|
|
1444
1537
|
|
|
1445
1538
|
try {
|
|
1539
|
+
assertOwned?.();
|
|
1446
1540
|
fd = fs.openSync(
|
|
1447
1541
|
staging,
|
|
1448
1542
|
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
|
|
@@ -1456,7 +1550,9 @@ export function publishManagedFileNoReplaceSync(
|
|
|
1456
1550
|
const staged = fs.fstatSync(fd, { bigint: true });
|
|
1457
1551
|
stagingIdentity = { dev: staged.dev, ino: staged.ino };
|
|
1458
1552
|
|
|
1553
|
+
assertOwned?.();
|
|
1459
1554
|
outcome = classifyNativePublishOutcome(renameNoReplacePath(staging, destination));
|
|
1555
|
+
assertOwned?.();
|
|
1460
1556
|
if (!outcome.ok) throw publishFailure(outcome);
|
|
1461
1557
|
|
|
1462
1558
|
const named = fs.lstatSync(destination, { bigint: true });
|
|
@@ -1492,6 +1588,7 @@ export function replaceManagedFileSync(
|
|
|
1492
1588
|
bytes: Uint8Array,
|
|
1493
1589
|
root: ManagedDirectoryRoot,
|
|
1494
1590
|
policy: ManagedSessionSecurityPolicy = "default",
|
|
1591
|
+
assertFence?: () => void,
|
|
1495
1592
|
): void {
|
|
1496
1593
|
const parent = path.dirname(destination);
|
|
1497
1594
|
ensureManagedDirectory(parent, root, policy);
|
|
@@ -1499,6 +1596,7 @@ export function replaceManagedFileSync(
|
|
|
1499
1596
|
let fd: number | undefined;
|
|
1500
1597
|
let stagedIdentity: { dev: bigint; ino: bigint } | undefined;
|
|
1501
1598
|
try {
|
|
1599
|
+
assertFence?.();
|
|
1502
1600
|
fd = fs.openSync(
|
|
1503
1601
|
staging,
|
|
1504
1602
|
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
|
|
@@ -1512,7 +1610,9 @@ export function replaceManagedFileSync(
|
|
|
1512
1610
|
const staged = fs.fstatSync(fd, { bigint: true });
|
|
1513
1611
|
stagedIdentity = { dev: staged.dev, ino: staged.ino };
|
|
1514
1612
|
assertManagedDirectoryRoot(root);
|
|
1613
|
+
assertFence?.();
|
|
1515
1614
|
fs.renameSync(staging, destination);
|
|
1615
|
+
assertFence?.();
|
|
1516
1616
|
assertManagedDirectoryRoot(root);
|
|
1517
1617
|
const named = fs.lstatSync(destination, { bigint: true });
|
|
1518
1618
|
if (!named.isFile() || named.isSymbolicLink() || named.dev !== staged.dev || named.ino !== staged.ino) {
|
package/src/session/messages.ts
CHANGED
|
@@ -194,14 +194,39 @@ 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(
|
|
197
|
+
export function createPreAdmissionArtifactSpillPreview(
|
|
198
|
+
fullText: string,
|
|
199
|
+
artifactId: string,
|
|
200
|
+
digest: string,
|
|
201
|
+
options: { maxInlineBytes?: number; fullOutput?: boolean } = {},
|
|
202
|
+
): string {
|
|
198
203
|
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
204
|
+
const maxInlineBytes = options.maxInlineBytes;
|
|
205
|
+
const referenceKind = options.fullOutput === false ? "retained output" : "full output";
|
|
206
|
+
let headBudget = PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES;
|
|
207
|
+
let tailBudget = PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES;
|
|
208
|
+
if (maxInlineBytes !== undefined) {
|
|
209
|
+
const receiptReserve = Buffer.byteLength(
|
|
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
|
+
}
|
|
205
230
|
}
|
|
206
231
|
|
|
207
232
|
/**
|
|
@@ -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 } from "./artifacts";
|
|
40
|
+
import { ArtifactManager, type ArtifactSaveReceipt } from "./artifacts";
|
|
41
41
|
import {
|
|
42
42
|
type BlobPutResult,
|
|
43
43
|
BlobStore,
|
|
@@ -895,6 +895,7 @@ export interface ResumeSessionIdentity {
|
|
|
895
895
|
sessionId: string;
|
|
896
896
|
dev: bigint;
|
|
897
897
|
ino: bigint;
|
|
898
|
+
nlink?: bigint;
|
|
898
899
|
size: number;
|
|
899
900
|
mtimeMs: number;
|
|
900
901
|
mtimeNs: bigint;
|
|
@@ -1132,7 +1133,7 @@ export function createReadonlySessionManager(manager: SessionManager): ReadonlyS
|
|
|
1132
1133
|
|
|
1133
1134
|
/** Internal artifact-writing capability. Read-only facades expose it only through a private weak-map lookup. */
|
|
1134
1135
|
export type SessionArtifactCapability = Readonly<
|
|
1135
|
-
Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "putBlob">
|
|
1136
|
+
Pick<SessionManager, "allocateArtifactPath" | "saveArtifact" | "getArtifactPath" | "putBlob">
|
|
1136
1137
|
>;
|
|
1137
1138
|
|
|
1138
1139
|
const sessionArtifactCapabilities = new WeakMap<SessionManager, SessionArtifactCapability>();
|
|
@@ -1149,6 +1150,7 @@ export function sessionArtifactCapability(value: unknown): SessionArtifactCapabi
|
|
|
1149
1150
|
capability = Object.freeze({
|
|
1150
1151
|
allocateArtifactPath: value.allocateArtifactPath.bind(value),
|
|
1151
1152
|
saveArtifact: value.saveArtifact.bind(value),
|
|
1153
|
+
getArtifactPath: value.getArtifactPath.bind(value),
|
|
1152
1154
|
putBlob: value.putBlob.bind(value),
|
|
1153
1155
|
});
|
|
1154
1156
|
sessionArtifactCapabilities.set(value, capability);
|
|
@@ -1831,6 +1833,7 @@ function retainedTreeSnapshotEquals(
|
|
|
1831
1833
|
entry.kind === other.kind &&
|
|
1832
1834
|
entry.dev === other.dev &&
|
|
1833
1835
|
entry.ino === other.ino &&
|
|
1836
|
+
entry.nlink === other.nlink &&
|
|
1834
1837
|
entry.size === other.size &&
|
|
1835
1838
|
entry.mtimeNs === other.mtimeNs &&
|
|
1836
1839
|
entry.ctimeNs === other.ctimeNs &&
|
|
@@ -1856,6 +1859,7 @@ function retainedTreeSnapshotEqualsAfterRename(
|
|
|
1856
1859
|
entry.kind === other.kind &&
|
|
1857
1860
|
entry.dev === other.dev &&
|
|
1858
1861
|
entry.ino === other.ino &&
|
|
1862
|
+
entry.nlink === other.nlink &&
|
|
1859
1863
|
entry.size === other.size &&
|
|
1860
1864
|
entry.mtimeNs === other.mtimeNs &&
|
|
1861
1865
|
(entry.relativePath === "" || entry.ctimeNs === other.ctimeNs) &&
|
|
@@ -2147,6 +2151,7 @@ function sameResumeIdentity(left: ResumeSessionIdentity, right: ResumeSessionIde
|
|
|
2147
2151
|
left.sessionId === right.sessionId &&
|
|
2148
2152
|
left.dev === right.dev &&
|
|
2149
2153
|
left.ino === right.ino &&
|
|
2154
|
+
left.nlink === right.nlink &&
|
|
2150
2155
|
left.size === right.size &&
|
|
2151
2156
|
left.mtimeMs === right.mtimeMs &&
|
|
2152
2157
|
left.mtimeNs === right.mtimeNs &&
|
|
@@ -2160,6 +2165,7 @@ function sameResumeStat(left: SessionStorageStat, right: SessionStorageStat): bo
|
|
|
2160
2165
|
right.isFile &&
|
|
2161
2166
|
left.dev === right.dev &&
|
|
2162
2167
|
left.ino === right.ino &&
|
|
2168
|
+
left.nlink === right.nlink &&
|
|
2163
2169
|
left.size === right.size &&
|
|
2164
2170
|
left.mtimeMs === right.mtimeMs &&
|
|
2165
2171
|
left.mtimeNs === right.mtimeNs
|
|
@@ -2356,6 +2362,7 @@ function inspectResumeSessionFile(
|
|
|
2356
2362
|
sessionId: header.id,
|
|
2357
2363
|
dev: snapshot.dev,
|
|
2358
2364
|
ino: snapshot.ino,
|
|
2365
|
+
nlink: snapshot.nlink,
|
|
2359
2366
|
size: snapshot.size,
|
|
2360
2367
|
mtimeMs: snapshot.mtimeMs,
|
|
2361
2368
|
mtimeNs: snapshot.mtimeNs,
|
|
@@ -2393,6 +2400,7 @@ function revalidateResumeSessionIdentity(
|
|
|
2393
2400
|
sessionId: expected.sessionId,
|
|
2394
2401
|
dev: snapshot.stat.dev,
|
|
2395
2402
|
ino: snapshot.stat.ino,
|
|
2403
|
+
nlink: snapshot.stat.nlink,
|
|
2396
2404
|
size: snapshot.stat.size,
|
|
2397
2405
|
mtimeMs: snapshot.stat.mtimeMs,
|
|
2398
2406
|
mtimeNs: snapshot.stat.mtimeNs,
|
|
@@ -4764,10 +4772,6 @@ export class SessionManager {
|
|
|
4764
4772
|
// Subagents adopt the parent's manager so artifact IDs are unique across the
|
|
4765
4773
|
// whole agent tree and all files land in the parent's artifacts dir.
|
|
4766
4774
|
#adoptedArtifactManager: ArtifactManager | null = null;
|
|
4767
|
-
// In-memory artifact fallback for non-persistent sessions (persist=false).
|
|
4768
|
-
// Keyed by sequential numeric ID string; mirrors the file-based ArtifactManager ID scheme.
|
|
4769
|
-
#inMemoryArtifacts: Map<string, string> | null = null;
|
|
4770
|
-
#inMemoryArtifactCounter = 0;
|
|
4771
4775
|
readonly #blobStore: BlobStore;
|
|
4772
4776
|
#residentTextBlobStore: BlobStore = new MemoryBlobStore();
|
|
4773
4777
|
#residentImageBlobStore: BlobStore;
|
|
@@ -5361,8 +5365,6 @@ export class SessionManager {
|
|
|
5361
5365
|
this.#flushed = false;
|
|
5362
5366
|
this.#needsFullRewriteOnNextPersist = false;
|
|
5363
5367
|
this.#ensuredOnDisk = false;
|
|
5364
|
-
this.#inMemoryArtifacts = null;
|
|
5365
|
-
this.#inMemoryArtifactCounter = 0;
|
|
5366
5368
|
this.#artifactManager = null;
|
|
5367
5369
|
this.#artifactManagerSessionFile = null;
|
|
5368
5370
|
this.#adoptedArtifactManager = null;
|
|
@@ -5581,8 +5583,6 @@ export class SessionManager {
|
|
|
5581
5583
|
flushed: this.#flushed,
|
|
5582
5584
|
needsFullRewriteOnNextPersist: this.#needsFullRewriteOnNextPersist,
|
|
5583
5585
|
ensuredOnDisk: this.#ensuredOnDisk,
|
|
5584
|
-
inMemoryArtifacts: this.#inMemoryArtifacts,
|
|
5585
|
-
inMemoryArtifactCounter: this.#inMemoryArtifactCounter,
|
|
5586
5586
|
artifactManager: this.#artifactManager,
|
|
5587
5587
|
artifactManagerSessionFile: this.#artifactManagerSessionFile,
|
|
5588
5588
|
adoptedArtifactManager: this.#adoptedArtifactManager,
|
|
@@ -5605,8 +5605,6 @@ export class SessionManager {
|
|
|
5605
5605
|
this.#flushed = previous.flushed;
|
|
5606
5606
|
this.#needsFullRewriteOnNextPersist = previous.needsFullRewriteOnNextPersist;
|
|
5607
5607
|
this.#ensuredOnDisk = previous.ensuredOnDisk;
|
|
5608
|
-
this.#inMemoryArtifacts = previous.inMemoryArtifacts;
|
|
5609
|
-
this.#inMemoryArtifactCounter = previous.inMemoryArtifactCounter;
|
|
5610
5608
|
this.#artifactManager = previous.artifactManager;
|
|
5611
5609
|
this.#artifactManagerSessionFile = previous.artifactManagerSessionFile;
|
|
5612
5610
|
this.#adoptedArtifactManager = previous.adoptedArtifactManager;
|
|
@@ -6052,8 +6050,6 @@ export class SessionManager {
|
|
|
6052
6050
|
this.#flushed = stage.flushed;
|
|
6053
6051
|
this.#needsFullRewriteOnNextPersist = false;
|
|
6054
6052
|
this.#ensuredOnDisk = stage.flushed;
|
|
6055
|
-
this.#inMemoryArtifacts = null;
|
|
6056
|
-
this.#inMemoryArtifactCounter = 0;
|
|
6057
6053
|
this.#artifactManager = null;
|
|
6058
6054
|
this.#artifactManagerSessionFile = null;
|
|
6059
6055
|
this.#adoptedArtifactManager = null;
|
|
@@ -7983,17 +7979,19 @@ export class SessionManager {
|
|
|
7983
7979
|
}
|
|
7984
7980
|
|
|
7985
7981
|
/**
|
|
7986
|
-
* Save artifact content under the current session and return artifact ID.
|
|
7987
|
-
*
|
|
7982
|
+
* Save artifact content under the current persisted session and return its artifact ID.
|
|
7983
|
+
* In-memory sessions cannot produce protocol-resolvable artifact references.
|
|
7988
7984
|
*/
|
|
7989
7985
|
async saveArtifact(content: string, toolType: string): Promise<string | undefined> {
|
|
7990
7986
|
const manager = this.#getOrCreateArtifactManager();
|
|
7991
7987
|
if (manager) return manager.save(content, toolType);
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
7995
|
-
|
|
7996
|
-
|
|
7988
|
+
return undefined;
|
|
7989
|
+
}
|
|
7990
|
+
|
|
7991
|
+
async saveArtifactReceipt(content: string, toolType: string): Promise<ArtifactSaveReceipt | undefined> {
|
|
7992
|
+
const manager = this.#getOrCreateArtifactManager();
|
|
7993
|
+
if (!manager) return undefined;
|
|
7994
|
+
return manager.saveWithReceipt(content, toolType);
|
|
7997
7995
|
}
|
|
7998
7996
|
|
|
7999
7997
|
/**
|