@gajae-code/coding-agent 0.11.4 → 0.11.5

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 (146) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/types/cli/notify-cli.d.ts +5 -1
  3. package/dist/types/config/settings-schema.d.ts +20 -0
  4. package/dist/types/export/html/template.generated.d.ts +1 -1
  5. package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
  6. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
  7. package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
  8. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
  9. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
  10. package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
  11. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  12. package/dist/types/harness-control-plane/owner.d.ts +3 -1
  13. package/dist/types/internal-urls/local-protocol.d.ts +18 -0
  14. package/dist/types/main.d.ts +2 -2
  15. package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
  16. package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
  17. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
  18. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  19. package/dist/types/modes/components/tool-transcript-format.d.ts +58 -0
  20. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
  21. package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
  22. package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
  23. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
  24. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
  25. package/dist/types/plan-mode/approved-plan.d.ts +1 -0
  26. package/dist/types/runtime-mcp/manager.d.ts +2 -0
  27. package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
  28. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
  29. package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
  30. package/dist/types/sdk/bus/config-commands.d.ts +8 -0
  31. package/dist/types/sdk/bus/config.d.ts +22 -0
  32. package/dist/types/sdk/bus/index.d.ts +162 -2
  33. package/dist/types/sdk/bus/notification-service.d.ts +71 -8
  34. package/dist/types/sdk/bus/session-control.d.ts +11 -8
  35. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
  36. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
  37. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
  38. package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
  39. package/dist/types/sdk/session.d.ts +16 -1
  40. package/dist/types/secrets/index.d.ts +4 -2
  41. package/dist/types/secrets/obfuscator.d.ts +1 -1
  42. package/dist/types/session/agent-session.d.ts +31 -2
  43. package/dist/types/session/artifacts.d.ts +30 -8
  44. package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
  45. package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
  46. package/dist/types/session/session-manager.d.ts +88 -18
  47. package/dist/types/session/session-storage.d.ts +27 -0
  48. package/dist/types/session/streaming-output.d.ts +4 -0
  49. package/dist/types/task/executor.d.ts +9 -0
  50. package/dist/types/tool-discovery/tool-index.d.ts +2 -0
  51. package/dist/types/tools/ask.d.ts +48 -0
  52. package/dist/types/tools/bash-skill-urls.d.ts +2 -2
  53. package/dist/types/tools/index.d.ts +4 -0
  54. package/package.json +9 -9
  55. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  56. package/src/cli/notify-cli.ts +40 -18
  57. package/src/commands/sdk.ts +7 -2
  58. package/src/config/settings-schema.ts +17 -0
  59. package/src/daemon/runtime.ts +2 -2
  60. package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
  61. package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
  62. package/src/defaults/gjc/skills/team/SKILL.md +7 -9
  63. package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
  64. package/src/eval/py/index.ts +2 -2
  65. package/src/export/html/template.generated.ts +1 -1
  66. package/src/extensibility/plugins/marketplace/manager.ts +94 -10
  67. package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
  68. package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
  69. package/src/gjc-runtime/deep-interview-state.ts +127 -3
  70. package/src/gjc-runtime/launch-tmux.ts +61 -6
  71. package/src/gjc-runtime/managed-owner-admission.ts +352 -0
  72. package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
  73. package/src/gjc-runtime/state-runtime.ts +38 -12
  74. package/src/gjc-runtime/team-runtime.ts +1 -1
  75. package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
  76. package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
  77. package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
  78. package/src/harness-control-plane/owner.ts +59 -3
  79. package/src/hindsight/client.ts +65 -2
  80. package/src/internal-urls/agent-protocol.ts +76 -5
  81. package/src/internal-urls/docs-index.generated.ts +7 -6
  82. package/src/internal-urls/local-protocol.ts +416 -18
  83. package/src/lsp/index.ts +4 -88
  84. package/src/main.ts +37 -13
  85. package/src/modes/acp/acp-agent.ts +8 -0
  86. package/src/modes/components/ansi-display-validator.ts +54 -0
  87. package/src/modes/components/notifications-settings-editor.ts +26 -2
  88. package/src/modes/components/session-observer-overlay.ts +40 -24
  89. package/src/modes/components/settings-selector.ts +3 -1
  90. package/src/modes/components/tool-transcript-format.ts +321 -0
  91. package/src/modes/components/transcript-viewer-overlay.ts +39 -17
  92. package/src/modes/controllers/goal-mode-controller.ts +20 -1
  93. package/src/modes/controllers/plan-mode-controller.ts +2 -0
  94. package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
  95. package/src/modes/controllers/selector-controller.ts +70 -8
  96. package/src/modes/interactive-mode.ts +1 -0
  97. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
  98. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
  99. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
  100. package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
  101. package/src/plan-mode/approved-plan.ts +3 -1
  102. package/src/prompts/agents/architect.md +1 -0
  103. package/src/prompts/agents/critic.md +1 -0
  104. package/src/prompts/system/custom-system-prompt.md +1 -1
  105. package/src/prompts/system/system-prompt.md +2 -1
  106. package/src/prompts/tools/lsp.md +3 -3
  107. package/src/runtime-mcp/manager.ts +16 -1
  108. package/src/runtime-mcp/oauth-discovery.ts +116 -26
  109. package/src/sdk/broker/session-index.ts +122 -64
  110. package/src/sdk/bus/chat-daemon-cli.ts +72 -10
  111. package/src/sdk/bus/chat-daemon-control.ts +526 -130
  112. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  113. package/src/sdk/bus/config-commands.ts +22 -0
  114. package/src/sdk/bus/config.ts +37 -0
  115. package/src/sdk/bus/index.ts +384 -92
  116. package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
  117. package/src/sdk/bus/notification-service.ts +284 -62
  118. package/src/sdk/bus/recent-activity.ts +14 -5
  119. package/src/sdk/bus/session-control.ts +150 -44
  120. package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
  121. package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
  122. package/src/sdk/bus/telegram-daemon-control.ts +294 -76
  123. package/src/sdk/bus/telegram-daemon.ts +1788 -267
  124. package/src/sdk/protocol/operation-inventory.generated.json +11 -0
  125. package/src/sdk/session.ts +155 -27
  126. package/src/secrets/index.ts +53 -10
  127. package/src/secrets/obfuscator.ts +21 -18
  128. package/src/secrets/regex.ts +2 -0
  129. package/src/session/agent-session.ts +495 -253
  130. package/src/session/artifacts.ts +188 -23
  131. package/src/session/context-estimation.ts +7 -2
  132. package/src/session/internal/managed-session-scope.ts +200 -38
  133. package/src/session/internal/managed-session-storage.ts +1152 -46
  134. package/src/session/session-manager.ts +1055 -148
  135. package/src/session/session-storage.ts +109 -11
  136. package/src/session/streaming-output.ts +6 -0
  137. package/src/task/executor.ts +71 -26
  138. package/src/task/index.ts +63 -20
  139. package/src/tool-discovery/tool-index.ts +18 -0
  140. package/src/tools/ask.ts +129 -32
  141. package/src/tools/bash-skill-urls.ts +14 -3
  142. package/src/tools/bash.ts +2 -2
  143. package/src/tools/index.ts +4 -0
  144. package/src/tools/plan-mode-guard.ts +1 -0
  145. package/src/tools/sqlite-reader.ts +3 -2
  146. package/src/tools/subagent.ts +2 -0
package/src/main.ts CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  VERSION,
21
21
  } from "@gajae-code/utils";
22
22
  import chalk from "chalk";
23
+ import { runCli } from "./cli";
23
24
  import type { Args } from "./cli/args";
24
25
  import { processFileArguments } from "./cli/file-processor";
25
26
  import { buildInitialMessage } from "./cli/initial-message";
@@ -36,6 +37,8 @@ import { BUNDLED_GROK_BUILD_EXTENSION_ID, getBundledGrokBuildExtensionFactory }
36
37
  import { initializeWithSettings } from "./discovery";
37
38
  import { exportFromFile } from "./export/html";
38
39
  import type { ExtensionUIContext } from "./extensibility/extensions/types";
40
+ import { admitManagedOwnerBeforeCli, completeManagedOwnerRecovery } from "./gjc-runtime/managed-owner-admission";
41
+ import { isManagedOwnerSupervisorArgv, runManagedOwnerSupervisor } from "./gjc-runtime/managed-owner-supervisor";
39
42
  import { persistCoordinatorRuntimeInputReady } from "./gjc-runtime/session-state-sidecar";
40
43
  import { isTmuxOwnerIsolationCliArgv, runTmuxOwnerIsolationCliFromStdin } from "./gjc-runtime/tmux-owner-isolation-cli";
41
44
  import type { AcpStartupOptions } from "./modes/acp/startup-options";
@@ -58,6 +61,7 @@ import type { AgentSession } from "./session/agent-session";
58
61
  import {
59
62
  type ResumeSessionIdentity,
60
63
  resolveResumableSession,
64
+ type SessionDestination,
61
65
  type SessionDirectoryMigrationPolicy,
62
66
  type SessionInfo,
63
67
  SessionManager,
@@ -535,7 +539,8 @@ type ListForResumePickerReadOnly = (cwd: string, sessionDir?: string) => Promise
535
539
  type SelectResumeSession = (sessions: SessionInfo[]) => Promise<SessionSelectionResult>;
536
540
  type OpenExistingSessionStrict = (
537
541
  identity: ResumeSessionIdentity,
538
- sessionDir?: string,
542
+ destination: SessionDestination,
543
+ storage?: undefined,
539
544
  migrationPolicy?: SessionDirectoryMigrationPolicy,
540
545
  ) => Promise<StrictSessionOpenResult>;
541
546
 
@@ -722,6 +727,10 @@ export async function createSessionManager(
722
727
  activeSettings: Settings = settings,
723
728
  ): Promise<SessionManager | undefined> {
724
729
  const migrationPolicy = activeSettings.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
730
+ const sessionDestination = () =>
731
+ parsed.sessionDir
732
+ ? SessionManager.explicitDestination(parsed.sessionDir)
733
+ : SessionManager.managedDestination(cwd, activeSettings.getAgentDir());
725
734
  if (parsed.resume === true) {
726
735
  return undefined;
727
736
  }
@@ -731,13 +740,13 @@ export async function createSessionManager(
731
740
  }
732
741
  const forkSource = parsed.fork;
733
742
  if (forkSource.includes("/") || forkSource.includes("\\") || forkSource.endsWith(".jsonl")) {
734
- return await SessionManager.forkFrom(forkSource, cwd, parsed.sessionDir, undefined, migrationPolicy);
743
+ return await SessionManager.forkFrom(forkSource, cwd, sessionDestination(), undefined, migrationPolicy);
735
744
  }
736
745
  const match = await resolveResumableSession(forkSource, cwd, parsed.sessionDir);
737
746
  if (!match) {
738
747
  throw new Error(`Session "${forkSource}" not found.`);
739
748
  }
740
- return await SessionManager.forkFrom(match.session.path, cwd, parsed.sessionDir, undefined, migrationPolicy);
749
+ return await SessionManager.forkFrom(match.session.path, cwd, sessionDestination(), undefined, migrationPolicy);
741
750
  }
742
751
 
743
752
  if (parsed.noSession) {
@@ -746,7 +755,12 @@ export async function createSessionManager(
746
755
  if (typeof parsed.resume === "string") {
747
756
  const sessionArg = parsed.resume;
748
757
  if (sessionArg.includes("/") || sessionArg.includes("\\") || sessionArg.endsWith(".jsonl")) {
749
- return await SessionManager.open(sessionArg, parsed.sessionDir, undefined, migrationPolicy);
758
+ return await SessionManager.open(
759
+ sessionArg,
760
+ SessionManager.explicitDestination(path.dirname(sessionArg)),
761
+ undefined,
762
+ migrationPolicy,
763
+ );
750
764
  }
751
765
  const match = await resolveResumableSession(sessionArg, cwd, parsed.sessionDir);
752
766
  if (!match) {
@@ -763,21 +777,21 @@ export async function createSessionManager(
763
777
  return await SessionManager.forkFrom(
764
778
  match.session.path,
765
779
  cwd,
766
- parsed.sessionDir,
780
+ sessionDestination(),
767
781
  undefined,
768
782
  migrationPolicy,
769
783
  );
770
784
  }
771
785
  }
772
- return await SessionManager.open(match.session.path, parsed.sessionDir, undefined, migrationPolicy);
786
+ return await SessionManager.open(match.session.path, sessionDestination(), undefined, migrationPolicy);
773
787
  }
774
788
  if (parsed.continue) {
775
- return await SessionManager.continueRecent(cwd, parsed.sessionDir, undefined, migrationPolicy);
789
+ return await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
776
790
  }
777
791
  // --resume without value is handled separately (needs picker UI)
778
792
  // If --session-dir provided without --continue/--resume, create new session there
779
793
  if (parsed.sessionDir) {
780
- return SessionManager.create(cwd, parsed.sessionDir);
794
+ return SessionManager.create(cwd, SessionManager.explicitDestination(parsed.sessionDir));
781
795
  }
782
796
  // A lifecycle `/session_create` child must start a FRESH session that adopts
783
797
  // the pre-allocated id (GJC_SESSION_ID), never auto-resume existing history in
@@ -795,14 +809,13 @@ export async function createSessionManager(
795
809
  // buildSessionOptions restores the session's model/thinking instead of
796
810
  // overriding them with CLI defaults.
797
811
  if (activeSettings.get("autoResume")) {
798
- const manager = await SessionManager.continueRecent(cwd, parsed.sessionDir, undefined, migrationPolicy);
812
+ const manager = await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
799
813
  if (manager.getEntries().length > 0) {
800
814
  parsed.continue = true;
801
815
  }
802
816
  return manager;
803
817
  }
804
- const sessionDir = parsed.sessionDir ?? SessionManager.getDefaultSessionDir(cwd, activeSettings.getAgentDir());
805
- return SessionManager.create(cwd, sessionDir);
818
+ return SessionManager.create(cwd, SessionManager.managedDestination(cwd, activeSettings.getAgentDir()));
806
819
  }
807
820
 
808
821
  async function maybeAutoChdir(parsed: Args): Promise<void> {
@@ -1106,7 +1119,9 @@ export async function runRootCommand(
1106
1119
  try {
1107
1120
  opened = await (deps.openExistingSessionStrict ?? SessionManager.openExistingStrict)(
1108
1121
  selection.identity,
1109
- parsedArgs.sessionDir,
1122
+ parsedArgs.sessionDir
1123
+ ? SessionManager.explicitDestination(parsedArgs.sessionDir)
1124
+ : SessionManager.managedDestination(resumeCwd, settings.getAgentDir()),
1110
1125
  undefined,
1111
1126
  resumeMigrationPolicy,
1112
1127
  );
@@ -1604,6 +1619,15 @@ export async function main(args: string[]): Promise<void> {
1604
1619
  await runTmuxOwnerIsolationCliFromStdin();
1605
1620
  return;
1606
1621
  }
1607
- const { runCli } = await import("./cli");
1622
+ if (isManagedOwnerSupervisorArgv(args)) {
1623
+ await runManagedOwnerSupervisor();
1624
+ return;
1625
+ }
1626
+ const admission = await admitManagedOwnerBeforeCli();
1627
+ if (admission.kind === "blocked") return;
1628
+ if (admission.kind === "recovery") {
1629
+ await completeManagedOwnerRecovery(admission.context);
1630
+ return;
1631
+ }
1608
1632
  await runCli(args.length === 0 ? ["launch"] : args);
1609
1633
  }
@@ -74,6 +74,7 @@ interface PromptWaiter {
74
74
  /** Highest inbound frame sequence already observed when the prompt was acknowledged. */
75
75
  boundary: number;
76
76
  correlation: PromptCorrelation;
77
+ messageProgress?: { textEmitted: boolean; thoughtEmitted: boolean };
77
78
  pendingTerminal?: PromptCorrelation;
78
79
  resolve: (response: PromptResponse) => void;
79
80
  reject: (error: Error) => void;
@@ -1298,9 +1299,16 @@ export class AcpAgent implements Agent {
1298
1299
  if (wirePayload) {
1299
1300
  for (const notification of mapAgentWireEventPayloadToAcpSessionUpdates(wirePayload as never, id, {
1300
1301
  cwd: record.cwd,
1302
+ getMessageProgress: message => {
1303
+ if (!activePrompt || !object(message)) return undefined;
1304
+ activePrompt.messageProgress ??= { textEmitted: false, thoughtEmitted: false };
1305
+ return activePrompt.messageProgress;
1306
+ },
1301
1307
  }))
1302
1308
  await this.#publishSessionUpdate(id, notification, adapter);
1303
1309
  }
1310
+ if (event.type === "message_end" && object(event.message)?.role === "assistant" && activePrompt)
1311
+ activePrompt.messageProgress = undefined;
1304
1312
  if (event.type === "agent_end") await this.#emitEndOfTurnUpdates(id, adapter);
1305
1313
  if (activePrompt) this.#settlePrompt(record, activePrompt);
1306
1314
  }
@@ -0,0 +1,54 @@
1
+ /** Removes every terminal control sequence except bounded SGR parameters (0-255). */
2
+ export function validateDisplayLine(line: string): string {
3
+ let result = "";
4
+ for (let index = 0; index < line.length; index++) {
5
+ const char = line[index]!;
6
+ const code = char.charCodeAt(0);
7
+ if (char === "\t") {
8
+ result += " ";
9
+ continue;
10
+ }
11
+ if (char === "\x1b") {
12
+ const next = line[index + 1];
13
+ if (next === "[") {
14
+ const match = /^\x1b\[([0-9]{1,3}(?:;[0-9]{1,3})*)m/.exec(line.slice(index));
15
+ if (match && match[0].length <= 64 && match[1]!.split(";").every(parameter => Number(parameter) <= 255)) {
16
+ result += match[0];
17
+ index += match[0].length - 1;
18
+ continue;
19
+ }
20
+ index = skipEscapeSequence(line, index + 2, /[@-~]/);
21
+ continue;
22
+ }
23
+ if (next === "]" || next === "P" || next === "_" || next === "^" || next === "X") {
24
+ index = skipStringControl(line, index + 2);
25
+ continue;
26
+ }
27
+ if (next === "_") {
28
+ index = skipStringControl(line, index + 2);
29
+ continue;
30
+ }
31
+ index += next ? 1 : 0;
32
+ continue;
33
+ }
34
+ if (code >= 0x80 && code <= 0x9f) continue;
35
+ if (code < 0x20 || code === 0x7f) continue;
36
+ result += char;
37
+ }
38
+ return result;
39
+ }
40
+
41
+ function skipEscapeSequence(text: string, start: number, final: RegExp): number {
42
+ for (let index = start; index < text.length; index++) {
43
+ if (final.test(text[index]!)) return index;
44
+ }
45
+ return text.length - 1;
46
+ }
47
+
48
+ function skipStringControl(text: string, start: number): number {
49
+ for (let index = start; index < text.length; index++) {
50
+ if (text[index] === "\x07") return index;
51
+ if (text[index] === "\x1b" && text[index + 1] === "\\") return index + 1;
52
+ }
53
+ return text.length - 1;
54
+ }
@@ -30,6 +30,8 @@ export interface NotificationsEditorPreferences {
30
30
  sessionScope: "all" | "primary";
31
31
  richEnabled: boolean;
32
32
  richDraftEnabled: boolean;
33
+ toolActivityEnabled: boolean;
34
+ streamingEnabled: boolean;
33
35
  }
34
36
 
35
37
  /** Secret-safe snapshot used to render the Notifications tab. */
@@ -50,6 +52,7 @@ export interface NotificationsEditorSetupInput {
50
52
  chatId?: string;
51
53
  richEnabled: boolean;
52
54
  richDraftEnabled: boolean;
55
+ streamingEnabled: boolean;
53
56
  }
54
57
 
55
58
  /**
@@ -62,6 +65,7 @@ export interface PreparedTelegramConfiguration {
62
65
  tokenFingerprint?: string;
63
66
  richEnabled: boolean;
64
67
  richDraftEnabled: boolean;
68
+ streamingEnabled: boolean;
65
69
  }
66
70
 
67
71
  export interface NotificationsPreflightResult {
@@ -155,7 +159,8 @@ type HomeActionId =
155
159
  | "recover"
156
160
  | "reconnect"
157
161
  | "remove"
158
- | "preferences";
162
+ | "preferences"
163
+ | "tool-activity";
159
164
 
160
165
  interface Action {
161
166
  id: HomeActionId | "telegram" | "external" | "save" | "save-inactive" | "cancel" | "confirm";
@@ -190,6 +195,8 @@ function emptyState(): NotificationsEditorState {
190
195
  sessionScope: "all",
191
196
  richEnabled: true,
192
197
  richDraftEnabled: false,
198
+ toolActivityEnabled: true,
199
+ streamingEnabled: true,
193
200
  },
194
201
  };
195
202
  }
@@ -549,6 +556,7 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
549
556
  chatId,
550
557
  richEnabled: preferences.richEnabled,
551
558
  richDraftEnabled: preferences.richDraftEnabled,
559
+ streamingEnabled: preferences.streamingEnabled,
552
560
  },
553
561
  signal,
554
562
  ),
@@ -928,6 +936,12 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
928
936
  case "refresh":
929
937
  draft.richDraftEnabled = !draft.richDraftEnabled;
930
938
  return;
939
+ case "tool-activity":
940
+ draft.toolActivityEnabled = !draft.toolActivityEnabled;
941
+ return;
942
+ case "test":
943
+ draft.streamingEnabled = !draft.streamingEnabled;
944
+ return;
931
945
  default:
932
946
  return;
933
947
  }
@@ -1068,6 +1082,16 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
1068
1082
  label: `Telegram rich drafts: ${draft.richDraftEnabled ? "on" : "off"}`,
1069
1083
  description: "Toggle the unsaved rich-draft preference.",
1070
1084
  },
1085
+ {
1086
+ id: "tool-activity",
1087
+ label: `Telegram tool activity: ${draft.toolActivityEnabled ? "on" : "off"}`,
1088
+ description: "Toggle tool start and completion notifications.",
1089
+ },
1090
+ {
1091
+ id: "test",
1092
+ label: `Telegram streaming: ${draft.streamingEnabled ? "on" : "off"}`,
1093
+ description: "Toggle the unsaved Telegram streaming preference.",
1094
+ },
1071
1095
  { id: "save", label: "Save preferences", description: "Atomically persist this preference draft." },
1072
1096
  { id: "cancel", label: "Cancel and discard draft", description: "Leave saved preferences unchanged." },
1073
1097
  ];
@@ -1162,7 +1186,7 @@ export class NotificationsSettingsEditorComponent implements Component, Focusabl
1162
1186
  );
1163
1187
  lines.push(
1164
1188
  this.#truncate(
1165
- ` Private chat: ${draft?.chatId ?? "(expired)"} rich: ${draft?.richEnabled ? "on" : "off"} drafts: ${draft?.richDraftEnabled ? "on" : "off"}`,
1189
+ ` Private chat: ${draft?.chatId ?? "(expired)"} rich: ${draft?.richEnabled ? "on" : "off"} drafts: ${draft?.richDraftEnabled ? "on" : "off"} streaming: ${draft?.streamingEnabled ? "on" : "off"}`,
1166
1190
  width,
1167
1191
  ),
1168
1192
  );
@@ -8,7 +8,11 @@ import type { SessionMessageEntry } from "../../session/session-manager";
8
8
  import { parseSessionEntries } from "../../session/session-manager";
9
9
  import type { ObservableSession, SessionObserverRegistry } from "../session-observer-registry";
10
10
  import { theme } from "../theme/theme";
11
- import { composeToolText } from "./tool-transcript-format";
11
+ import {
12
+ buildToolTranscriptEntry,
13
+ composeToolText,
14
+ createToolTranscriptRenderDescriptor,
15
+ } from "./tool-transcript-format";
12
16
  import { type TranscriptViewerEntry, TranscriptViewerOverlay } from "./transcript-viewer-overlay";
13
17
 
14
18
  /** Session-observer adapter. The shared viewer owns navigation and fold state. */
@@ -148,7 +152,7 @@ export class SessionObserverOverlayComponent extends TranscriptViewerOverlay {
148
152
  }
149
153
  }
150
154
 
151
- function entriesFromMessages(entries: readonly SessionMessageEntry[]): TranscriptViewerEntry[] {
155
+ export function entriesFromMessages(entries: readonly SessionMessageEntry[]): TranscriptViewerEntry[] {
152
156
  const results = new Map<string, ToolResultMessage>();
153
157
  for (const entry of entries)
154
158
  if (entry.message.role === "toolResult") results.set(entry.message.toolCallId, entry.message);
@@ -190,30 +194,42 @@ function entriesFromMessages(entries: readonly SessionMessageEntry[]): Transcrip
190
194
  .map(part => part.text)
191
195
  .join("\n")
192
196
  .trim() ?? "";
193
- const text = composeToolText({
194
- name: content.name,
195
- args: content.arguments,
196
- intent: content.intent,
197
- resultText,
198
- isError: result?.isError ?? false,
199
- hasResult: results.has(content.id),
200
- });
201
- output.push({
202
- id: `tool:${content.id}`,
203
- kind: "tool",
204
- label: content.name,
205
- payload: {
206
- text,
207
- metadata: {
197
+ const canonicalPayload = {
198
+ text: composeToolText({
199
+ name: content.name,
200
+ args: content.arguments,
201
+ intent: content.intent,
202
+ resultText,
203
+ isError: result?.isError ?? false,
204
+ hasResult: results.has(content.id),
205
+ }),
206
+ metadata: {
207
+ name: content.name,
208
+ arguments: content.arguments,
209
+ intent: content.intent,
210
+ resultText,
211
+ isError: result?.isError ?? false,
212
+ hasResult: results.has(content.id),
213
+ detailsData: result?.details,
214
+ },
215
+ source: { call: content, result },
216
+ };
217
+ output.push(
218
+ buildToolTranscriptEntry({
219
+ canonicalPayload,
220
+ renderDescriptor: createToolTranscriptRenderDescriptor({
208
221
  name: content.name,
209
- arguments: content.arguments,
222
+ args: content.arguments,
210
223
  intent: content.intent,
211
- isError: result?.isError ?? false,
212
- },
213
- source: { call: content, result },
214
- },
215
- foldable: true,
216
- });
224
+ resultContent: resultText,
225
+ isError: result?.isError,
226
+ hasResult: results.has(content.id),
227
+ detailsData: result?.details,
228
+ }),
229
+ capabilities: { copyable: true, foldable: true, rawViewable: true },
230
+ identity: { id: `tool:${content.id}`, label: content.name, display: "full" },
231
+ }),
232
+ );
217
233
  }
218
234
  });
219
235
  }
@@ -669,6 +669,8 @@ export interface SettingsRuntimeContext {
669
669
  cwd: string;
670
670
  /** Whether this terminal can render the pet overlay. */
671
671
  petAvailable?: boolean;
672
+ /** Terminal environment used to select unavailable-pet guidance. Omitted in production to use Bun.env. */
673
+ terminalEnv?: NodeJS.ProcessEnv;
672
674
  }
673
675
 
674
676
  /** Status line settings subset for preview */
@@ -896,7 +898,7 @@ export class SettingsSelectorComponent extends Container {
896
898
  options = createPetSelectItems(options, currentValue, petAvailable);
897
899
  // Unsupported terminals must see the same actionable guidance the
898
900
  // startup notice and /pet show, not only dimmed option descriptions.
899
- if (!petAvailable) description = getPetUnavailableWarning();
901
+ if (!petAvailable) description = getPetUnavailableWarning(this.context.terminalEnv);
900
902
  }
901
903
  // Preview handlers
902
904
  let onPreview: ((value: string) => void | Promise<void>) | undefined;