@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.
Files changed (112) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/types/cli/notify-cli.d.ts +42 -1
  3. package/dist/types/cli/update-cli.d.ts +9 -3
  4. package/dist/types/commands/notify.d.ts +6 -0
  5. package/dist/types/config/file-lock.d.ts +3 -0
  6. package/dist/types/coordinator/contract.d.ts +1 -1
  7. package/dist/types/coordinator-mcp/question-state.d.ts +3 -1
  8. package/dist/types/extensibility/extensions/index.d.ts +1 -0
  9. package/dist/types/extensibility/extensions/ouroboros-ooo-bridge.d.ts +13 -0
  10. package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -1
  11. package/dist/types/extensibility/extensions/types.d.ts +2 -0
  12. package/dist/types/main.d.ts +6 -2
  13. package/dist/types/modes/components/session-selector.d.ts +1 -1
  14. package/dist/types/modes/utils/host-status.d.ts +23 -0
  15. package/dist/types/runtime-mcp/types.d.ts +2 -0
  16. package/dist/types/sdk/broker/lifecycle.d.ts +12 -0
  17. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -0
  18. package/dist/types/sdk/bus/chat-daemon-command-channel.d.ts +244 -0
  19. package/dist/types/sdk/bus/chat-daemon-control.d.ts +11 -1
  20. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +7 -0
  21. package/dist/types/sdk/bus/control-plane-frames.d.ts +43 -0
  22. package/dist/types/sdk/bus/conversation-store.d.ts +18 -0
  23. package/dist/types/sdk/bus/existing-thread-readiness.d.ts +71 -0
  24. package/dist/types/sdk/bus/notification-service.d.ts +10 -0
  25. package/dist/types/sdk/bus/slack-daemon.d.ts +27 -0
  26. package/dist/types/sdk/bus/slack-live-provider.d.ts +11 -0
  27. package/dist/types/sdk/bus/slack-provider.d.ts +14 -0
  28. package/dist/types/sdk/bus/slack-thread-binding.d.ts +140 -0
  29. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +2 -0
  30. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +24 -7
  31. package/dist/types/sdk/bus/telegram-daemon.d.ts +104 -16
  32. package/dist/types/sdk/bus/telegram-reference.d.ts +10 -1
  33. package/dist/types/sdk/bus/topic-registry.d.ts +130 -85
  34. package/dist/types/sdk/host/host.d.ts +52 -0
  35. package/dist/types/sdk/lifecycle-session.d.ts +6 -0
  36. package/dist/types/sdk/session-activation.d.ts +41 -0
  37. package/dist/types/sdk/session.d.ts +18 -0
  38. package/dist/types/sdk/startup-capability.d.ts +8 -1
  39. package/dist/types/session/agent-session.d.ts +7 -0
  40. package/dist/types/session/artifacts.d.ts +2 -2
  41. package/dist/types/session/session-manager.d.ts +23 -5
  42. package/dist/types/tools/index.d.ts +8 -0
  43. package/dist/types/utils/npm-registry.d.ts +71 -0
  44. package/examples/extensions/README.md +38 -4
  45. package/examples/extensions/ooo-bridge.ts +15 -0
  46. package/package.json +8 -8
  47. package/scripts/generate-sdk-operation-inventory.ts +4 -0
  48. package/src/async/job-manager.ts +9 -2
  49. package/src/cli/notify-cli.ts +226 -2
  50. package/src/cli/update-cli.ts +90 -23
  51. package/src/commands/notify.ts +27 -3
  52. package/src/commands/sdk.ts +1 -0
  53. package/src/config/file-lock-gc.ts +18 -0
  54. package/src/config/file-lock.ts +61 -16
  55. package/src/config/model-profiles.ts +31 -15
  56. package/src/coordinator/contract.ts +1 -0
  57. package/src/coordinator-mcp/question-state.ts +2 -0
  58. package/src/coordinator-mcp/server.ts +358 -7
  59. package/src/defaults/gjc/skills/ultragoal/SKILL.md +2 -0
  60. package/src/extensibility/extensions/index.ts +1 -0
  61. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +235 -0
  62. package/src/extensibility/extensions/prefix-command-bridge.ts +4 -9
  63. package/src/extensibility/extensions/runner.ts +15 -2
  64. package/src/extensibility/extensions/types.ts +2 -0
  65. package/src/gjc-runtime/launch-tmux.ts +53 -1
  66. package/src/internal-urls/docs-index.generated.ts +11 -10
  67. package/src/main.ts +203 -67
  68. package/src/modes/DESIGN.md +8 -5
  69. package/src/modes/components/footer.ts +10 -18
  70. package/src/modes/components/session-selector.ts +26 -3
  71. package/src/modes/components/status-line/gh.ts +23 -2
  72. package/src/modes/controllers/event-controller.ts +3 -0
  73. package/src/modes/controllers/extension-ui-controller.ts +2 -0
  74. package/src/modes/controllers/input-controller.ts +11 -0
  75. package/src/modes/controllers/selector-controller.ts +8 -2
  76. package/src/modes/interactive-mode.ts +7 -1
  77. package/src/modes/utils/host-status.ts +38 -0
  78. package/src/runtime-mcp/types.ts +2 -0
  79. package/src/sdk/broker/lifecycle.ts +60 -3
  80. package/src/sdk/bus/chat-daemon-cli.ts +42 -0
  81. package/src/sdk/bus/chat-daemon-command-channel.ts +921 -0
  82. package/src/sdk/bus/chat-daemon-control.ts +16 -3
  83. package/src/sdk/bus/chat-daemon-runtime.ts +257 -21
  84. package/src/sdk/bus/control-plane-frames.ts +51 -0
  85. package/src/sdk/bus/conversation-store.ts +29 -1
  86. package/src/sdk/bus/existing-thread-readiness.ts +155 -0
  87. package/src/sdk/bus/index.ts +70 -0
  88. package/src/sdk/bus/notification-service.ts +28 -1
  89. package/src/sdk/bus/rich-render.ts +3 -2
  90. package/src/sdk/bus/slack-daemon.ts +99 -4
  91. package/src/sdk/bus/slack-live-provider.ts +32 -2
  92. package/src/sdk/bus/slack-provider.ts +11 -0
  93. package/src/sdk/bus/slack-thread-binding.ts +641 -0
  94. package/src/sdk/bus/telegram-daemon-cli.ts +11 -0
  95. package/src/sdk/bus/telegram-daemon-contract.ts +24 -7
  96. package/src/sdk/bus/telegram-daemon.ts +1961 -497
  97. package/src/sdk/bus/telegram-reference.ts +42 -6
  98. package/src/sdk/bus/topic-registry.ts +674 -181
  99. package/src/sdk/host/host.ts +180 -1
  100. package/src/sdk/lifecycle-session.ts +8 -2
  101. package/src/sdk/protocol/operation-inventory.generated.json +22 -0
  102. package/src/sdk/session-activation.ts +127 -0
  103. package/src/sdk/session.ts +139 -28
  104. package/src/sdk/startup-capability.ts +10 -1
  105. package/src/session/agent-session.ts +220 -9
  106. package/src/session/artifacts.ts +56 -13
  107. package/src/session/internal/managed-session-storage.ts +206 -7
  108. package/src/session/session-manager.ts +148 -38
  109. package/src/setup/provider-presets.json +1 -1
  110. package/src/task/index.ts +92 -44
  111. package/src/tools/index.ts +8 -0
  112. package/src/utils/npm-registry.ts +660 -0
package/src/task/index.ts CHANGED
@@ -485,10 +485,12 @@ interface SessionLifetimeArtifactsState {
485
485
  originalGetArtifactsDir?: ToolSession["getArtifactsDir"];
486
486
  originalGetAuthorizedArtifactsDirs?: ToolSession["getAuthorizedArtifactsDirs"];
487
487
  originalGetArtifactManager?: ToolSession["getArtifactManager"];
488
+ originalIsArtifactManagerAuthorized?: ToolSession["isArtifactManagerAuthorized"];
488
489
  originalAgentOutputManager?: AgentOutputManager;
489
490
  installedGetArtifactsDir?: ToolSession["getArtifactsDir"];
490
491
  installedGetAuthorizedArtifactsDirs?: ToolSession["getAuthorizedArtifactsDirs"];
491
492
  installedGetArtifactManager?: ToolSession["getArtifactManager"];
493
+ installedIsArtifactManagerAuthorized?: ToolSession["isArtifactManagerAuthorized"];
492
494
  installedAgentOutputManager?: AgentOutputManager;
493
495
  }
494
496
 
@@ -568,6 +570,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
568
570
  return this.session.settings.get("task.simple");
569
571
  }
570
572
 
573
+ /**
574
+ * The artifact store shared by this whole agent tree, when the session proves
575
+ * the exact manager relationship. Path containment is not authority: an
576
+ * unrelated manager can choose a lexically nested root or session filename.
577
+ */
578
+ #sharedArtifactStore(): { dir: string; manager: ArtifactManager } | null {
579
+ const manager = this.session.getArtifactManager?.() ?? null;
580
+ if (!manager || this.session.isArtifactManagerAuthorized?.(manager) !== true) return null;
581
+ return { dir: path.resolve(manager.dir), manager };
582
+ }
583
+
571
584
  /**
572
585
  * Ensure a session-lifetime artifact directory when the parent has no session file.
573
586
  * Returns null only when durable allocation fails (mkdir error) — callers must not
@@ -576,15 +589,24 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
576
589
  async #ensureSessionLifetimeArtifacts(): Promise<{ dir: string; manager: ArtifactManager } | null> {
577
590
  const state = sessionLifetimeArtifactsState(this.session);
578
591
  if (state.authorized && state.dir && state.manager) return { dir: state.dir, manager: state.manager };
592
+ if (this.session.ensureArtifactManager) {
593
+ const manager = await this.session.ensureArtifactManager();
594
+ if (manager && this.session.isArtifactManagerAuthorized?.(manager) === true) {
595
+ const dir = path.resolve(manager.dir);
596
+ state.dir = dir;
597
+ state.manager = manager;
598
+ if (!(await this.#authorizeSessionLifetimeArtifacts(state, dir, manager, false, false))) return null;
599
+ return { dir, manager };
600
+ }
601
+ }
579
602
 
580
603
  const sessionArtifactsDir = this.session.getArtifactsDir?.() ?? null;
581
604
  if (sessionArtifactsDir) {
582
- const existingManager = this.session.getArtifactManager?.() ?? null;
583
- if (existingManager && path.resolve(existingManager.dir) !== path.resolve(sessionArtifactsDir)) return null;
584
- const manager = existingManager ?? new ArtifactManager(sessionArtifactsDir);
605
+ const manager = new ArtifactManager(sessionArtifactsDir);
585
606
  state.dir = sessionArtifactsDir;
586
607
  state.manager = manager;
587
- if (!(await this.#authorizeSessionLifetimeArtifacts(state, sessionArtifactsDir, manager, false))) return null;
608
+ if (!(await this.#authorizeSessionLifetimeArtifacts(state, sessionArtifactsDir, manager, false, true)))
609
+ return null;
588
610
  return { dir: sessionArtifactsDir, manager };
589
611
  }
590
612
  if (!this.session.registerSessionCleanup) return null;
@@ -609,7 +631,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
609
631
  }
610
632
  state.dir = dir;
611
633
  state.manager = new ArtifactManager(dir);
612
- if (!(await this.#authorizeSessionLifetimeArtifacts(state, dir, state.manager, true))) return null;
634
+ if (!(await this.#authorizeSessionLifetimeArtifacts(state, dir, state.manager, true, true))) return null;
613
635
  return dir;
614
636
  }
615
637
 
@@ -623,11 +645,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
623
645
  dir: string,
624
646
  manager: ArtifactManager,
625
647
  owned: boolean,
648
+ adoptIntoSessionOwner: boolean,
626
649
  ): Promise<boolean> {
627
650
  if (state.authorized) return true;
628
651
  state.originalGetArtifactsDir = this.session.getArtifactsDir;
629
652
  state.originalGetAuthorizedArtifactsDirs = this.session.getAuthorizedArtifactsDirs;
630
653
  state.originalGetArtifactManager = this.session.getArtifactManager;
654
+ state.originalIsArtifactManagerAuthorized = this.session.isArtifactManagerAuthorized;
631
655
  state.originalAgentOutputManager = this.session.agentOutputManager;
632
656
 
633
657
  state.installedGetArtifactsDir = () => state.originalGetArtifactsDir?.() ?? dir;
@@ -643,6 +667,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
643
667
  return dirs;
644
668
  };
645
669
  state.installedGetArtifactManager = () => manager;
670
+ state.installedIsArtifactManagerAuthorized = candidate =>
671
+ candidate === manager || state.originalIsArtifactManagerAuthorized?.(candidate) === true;
646
672
  if (owned || !this.session.agentOutputManager) {
647
673
  state.installedAgentOutputManager = new AgentOutputManager(() => this.session.getArtifactsDir?.() ?? null, {
648
674
  parentPrefix: owned ? state.outputPrefix : undefined,
@@ -653,56 +679,74 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
653
679
  let cleanupRan = false;
654
680
  const cleanup = async () => {
655
681
  cleanupRan = true;
656
- try {
657
- if (owned) await fs.rm(dir, { recursive: true, force: true });
658
- } finally {
659
- if (this.session.getArtifactsDir === state.installedGetArtifactsDir)
660
- this.session.getArtifactsDir = state.originalGetArtifactsDir;
661
- if (this.session.getAuthorizedArtifactsDirs === state.installedGetAuthorizedArtifactsDirs)
662
- this.session.getAuthorizedArtifactsDirs = state.originalGetAuthorizedArtifactsDirs;
663
- if (this.session.getArtifactManager === state.installedGetArtifactManager)
664
- this.session.getArtifactManager = state.originalGetArtifactManager;
665
- if (this.session.agentOutputManager === state.installedAgentOutputManager)
666
- this.session.agentOutputManager = state.originalAgentOutputManager;
667
- sessionLifetimeArtifacts.delete(this.session);
668
- }
682
+ this.session.releaseArtifactManager?.(manager);
683
+ if (this.session.getArtifactsDir === state.installedGetArtifactsDir)
684
+ this.session.getArtifactsDir = state.originalGetArtifactsDir;
685
+ if (this.session.getAuthorizedArtifactsDirs === state.installedGetAuthorizedArtifactsDirs)
686
+ this.session.getAuthorizedArtifactsDirs = state.originalGetAuthorizedArtifactsDirs;
687
+ if (this.session.getArtifactManager === state.installedGetArtifactManager)
688
+ this.session.getArtifactManager = state.originalGetArtifactManager;
689
+ if (this.session.isArtifactManagerAuthorized === state.installedIsArtifactManagerAuthorized)
690
+ this.session.isArtifactManagerAuthorized = state.originalIsArtifactManagerAuthorized;
691
+ if (this.session.agentOutputManager === state.installedAgentOutputManager)
692
+ this.session.agentOutputManager = state.originalAgentOutputManager;
693
+ sessionLifetimeArtifacts.delete(this.session);
694
+ if (owned) await fs.rm(dir, { recursive: true, force: true });
669
695
  };
670
- if (owned) {
671
- const registerCleanup = this.session.registerSessionCleanup;
672
- if (!registerCleanup) {
673
- state.dir = undefined;
674
- state.manager = undefined;
675
- state.ensurePromise = undefined;
676
- sessionLifetimeArtifacts.delete(this.session);
677
- await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
678
- return false;
679
- }
696
+ const registerCleanup = this.session.registerSessionCleanup;
697
+ if (!registerCleanup) {
698
+ state.dir = undefined;
699
+ state.manager = undefined;
700
+ state.ensurePromise = undefined;
701
+ sessionLifetimeArtifacts.delete(this.session);
702
+ if (owned) await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
703
+ return false;
704
+ }
705
+ try {
706
+ registerCleanup(cleanup);
707
+ } catch {
708
+ state.dir = undefined;
709
+ state.manager = undefined;
710
+ state.ensurePromise = undefined;
711
+ sessionLifetimeArtifacts.delete(this.session);
712
+ if (owned) await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
713
+ return false;
714
+ }
715
+ if (cleanupRan) return false;
716
+ state.cleanupRegistered = true;
717
+ if (adoptIntoSessionOwner && this.session.adoptArtifactManager) {
680
718
  try {
681
- registerCleanup(cleanup);
719
+ this.session.adoptArtifactManager(manager);
682
720
  } catch {
683
- state.dir = undefined;
684
- state.manager = undefined;
685
- state.ensurePromise = undefined;
686
- sessionLifetimeArtifacts.delete(this.session);
687
- await fs.rm(dir, { recursive: true, force: true }).catch(() => {});
721
+ await cleanup();
722
+ return false;
723
+ }
724
+ if (
725
+ this.session.getArtifactManager?.() !== manager ||
726
+ this.session.isArtifactManagerAuthorized?.(manager) !== true
727
+ ) {
728
+ await cleanup();
688
729
  return false;
689
730
  }
690
- if (cleanupRan) return false;
691
- state.cleanupRegistered = true;
692
731
  }
693
732
 
694
733
  state.authorized = true;
695
734
  this.session.getArtifactsDir = state.installedGetArtifactsDir;
696
735
  this.session.getAuthorizedArtifactsDirs = state.installedGetAuthorizedArtifactsDirs;
697
736
  this.session.getArtifactManager = state.installedGetArtifactManager;
737
+ this.session.isArtifactManagerAuthorized = state.installedIsArtifactManagerAuthorized;
698
738
  if (state.installedAgentOutputManager) this.session.agentOutputManager = state.installedAgentOutputManager;
699
739
  return true;
700
740
  }
701
741
 
702
742
  /**
703
743
  * Resolve the effective artifacts directory for this task batch.
704
- * File-backed sessions use the session artifacts path; in-memory parents use
705
- * the session-lifetime durable temp root when allocation succeeds.
744
+ *
745
+ * The session's ArtifactManager is authoritative when present: a subagent
746
+ * adopts its parent's manager, so honouring it keeps the whole agent tree on
747
+ * one artifact directory and one ID space instead of giving each nesting
748
+ * level a private store. Sessions without a manager fall back to the session
749
+ * artifacts path, then to the session-lifetime durable temp root.
706
750
  */
707
751
  async #resolveEffectiveArtifactsDir(): Promise<{
708
752
  sessionArtifactsDir: string | null;
@@ -710,19 +754,23 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
710
754
  effectiveArtifactsDir: string | undefined;
711
755
  parentArtifactManager: ArtifactManager | undefined;
712
756
  }> {
757
+ const shared = this.#sharedArtifactStore();
758
+ if (shared) {
759
+ return {
760
+ sessionArtifactsDir: shared.dir,
761
+ durableArtifactsDir: null,
762
+ effectiveArtifactsDir: shared.dir,
763
+ parentArtifactManager: shared.manager,
764
+ };
765
+ }
713
766
  const sessionFile = this.session.getSessionFile();
714
767
  const sessionArtifactsDir = sessionFile ? sessionFile.slice(0, -6) : null;
715
768
  if (sessionArtifactsDir) {
716
- const candidateManager = this.session.getArtifactManager?.() ?? undefined;
717
- const parentArtifactManager =
718
- candidateManager && path.resolve(candidateManager.dir) === path.resolve(sessionArtifactsDir)
719
- ? candidateManager
720
- : undefined;
721
769
  return {
722
770
  sessionArtifactsDir,
723
771
  durableArtifactsDir: null,
724
772
  effectiveArtifactsDir: sessionArtifactsDir,
725
- parentArtifactManager,
773
+ parentArtifactManager: undefined,
726
774
  };
727
775
  }
728
776
  const durable = await this.#ensureSessionLifetimeArtifacts();
@@ -296,6 +296,14 @@ export interface ToolSession {
296
296
  getAuthorizedArtifactsDirs?: () => readonly string[];
297
297
  /** Get the ArtifactManager backing this session (shared across parent + subagents). */
298
298
  getArtifactManager?: () => ArtifactManager | null;
299
+ /** Prove that an ArtifactManager belongs to this concrete session or its explicitly adopted parent tree. */
300
+ isArtifactManagerAuthorized?: (manager: ArtifactManager) => boolean;
301
+ /** Adopt a task-created fallback manager into the concrete session owner. */
302
+ adoptArtifactManager?: (manager: ArtifactManager) => void;
303
+ /** Linearizably establish the concrete session owner's canonical artifact manager. */
304
+ ensureArtifactManager?: () => Promise<ArtifactManager | null>;
305
+ /** Release a task-created fallback manager when its logical session ends. */
306
+ releaseArtifactManager?: (manager: ArtifactManager) => void;
299
307
  /** Register teardown work owned by the current logical session. */
300
308
  registerSessionCleanup?: (cleanup: () => Promise<void> | void) => () => void;
301
309
  /** Allocate a new artifact path and ID for session-scoped truncated output. */