@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.
Files changed (70) hide show
  1. package/CHANGELOG.md +3 -1
  2. package/dist/types/async/job-manager.d.ts +36 -1
  3. package/dist/types/eval/backend.d.ts +2 -2
  4. package/dist/types/exec/bash-executor.d.ts +8 -10
  5. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +0 -36
  6. package/dist/types/internal-urls/registry-helpers.d.ts +0 -18
  7. package/dist/types/sdk/bus/chat-daemon-control.d.ts +1 -3
  8. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +2 -4
  9. package/dist/types/sdk/host/host.d.ts +2 -23
  10. package/dist/types/sdk/host/reverse-leases.d.ts +4 -7
  11. package/dist/types/session/artifacts.d.ts +5 -8
  12. package/dist/types/session/client-bridge.d.ts +0 -14
  13. package/dist/types/session/internal/managed-session-storage.d.ts +2 -5
  14. package/dist/types/session/messages.d.ts +1 -4
  15. package/dist/types/session/session-manager.d.ts +4 -5
  16. package/dist/types/session/streaming-output.d.ts +0 -32
  17. package/dist/types/task/receipt.d.ts +1 -2
  18. package/dist/types/task/types.d.ts +43 -178
  19. package/dist/types/tools/bash.d.ts +1 -1
  20. package/dist/types/tools/fetch.d.ts +0 -3
  21. package/dist/types/tools/output-meta.d.ts +5 -37
  22. package/dist/types/tools/subagent.d.ts +15 -0
  23. package/dist/types/tools/todo-write.d.ts +3 -0
  24. package/dist/types/tools/tool-result.d.ts +0 -1
  25. package/package.json +8 -8
  26. package/src/async/job-manager.ts +430 -46
  27. package/src/cursor.ts +13 -20
  28. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -3
  29. package/src/eval/backend.ts +2 -2
  30. package/src/exec/bash-executor.ts +151 -425
  31. package/src/gjc-runtime/ultragoal-runtime.ts +3 -280
  32. package/src/gjc-runtime/workflow-manifest.generated.json +1 -40
  33. package/src/gjc-runtime/workflow-manifest.ts +0 -12
  34. package/src/internal-urls/agent-protocol.ts +38 -98
  35. package/src/internal-urls/artifact-protocol.ts +17 -28
  36. package/src/internal-urls/docs-index.generated.ts +1 -1
  37. package/src/internal-urls/registry-helpers.ts +0 -62
  38. package/src/modes/acp/acp-agent.ts +0 -24
  39. package/src/modes/acp/acp-client-bridge.ts +11 -154
  40. package/src/prompts/tools/bash.md +2 -2
  41. package/src/sdk/acp/adapter.ts +9 -200
  42. package/src/sdk/bus/chat-daemon-control.ts +3 -5
  43. package/src/sdk/bus/index.ts +8 -324
  44. package/src/sdk/bus/telegram-daemon-contract.ts +2 -4
  45. package/src/sdk/host/host.ts +9 -187
  46. package/src/sdk/host/reverse-leases.ts +42 -249
  47. package/src/session/agent-session.ts +15 -178
  48. package/src/session/artifacts.ts +74 -211
  49. package/src/session/client-bridge.ts +0 -15
  50. package/src/session/internal/managed-session-storage.ts +5 -104
  51. package/src/session/messages.ts +7 -32
  52. package/src/session/session-manager.ts +21 -12
  53. package/src/session/streaming-output.ts +40 -198
  54. package/src/task/index.ts +230 -64
  55. package/src/task/receipt.ts +2 -4
  56. package/src/task/render.ts +0 -30
  57. package/src/task/types.ts +10 -34
  58. package/src/tools/bash.ts +239 -818
  59. package/src/tools/eval.ts +4 -99
  60. package/src/tools/fetch.ts +15 -77
  61. package/src/tools/find.ts +3 -7
  62. package/src/tools/output-meta.ts +114 -696
  63. package/src/tools/search.ts +4 -9
  64. package/src/tools/subagent.ts +97 -75
  65. package/src/tools/todo-write.ts +47 -0
  66. package/src/tools/tool-result.ts +0 -1
  67. package/dist/types/gjc-runtime/ultragoal-review-source.d.ts +0 -83
  68. package/dist/types/utils/artifact-id.d.ts +0 -2
  69. package/src/gjc-runtime/ultragoal-review-source.ts +0 -378
  70. 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) {
@@ -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 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
- }
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, type ArtifactSaveReceipt } from "./artifacts";
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" | "getArtifactPath" | "putBlob">
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 persisted session and return its artifact ID.
7983
- * In-memory sessions cannot produce protocol-resolvable artifact references.
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
- 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);
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
  /**