@gajae-code/coding-agent 0.11.4 → 0.11.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.
Files changed (154) 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 +31 -1
  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 +42 -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 +11 -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 +98 -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/session/yield-queue.d.ts +8 -0
  50. package/dist/types/task/executor.d.ts +9 -0
  51. package/dist/types/tool-discovery/tool-index.d.ts +2 -0
  52. package/dist/types/tools/ask.d.ts +48 -0
  53. package/dist/types/tools/bash-skill-urls.d.ts +2 -2
  54. package/dist/types/tools/index.d.ts +4 -0
  55. package/package.json +9 -9
  56. package/scripts/generate-sdk-operation-inventory.ts +3 -0
  57. package/src/cli/notify-cli.ts +40 -18
  58. package/src/commands/sdk.ts +7 -2
  59. package/src/config/settings-schema.ts +31 -1
  60. package/src/daemon/runtime.ts +2 -2
  61. package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
  62. package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
  63. package/src/defaults/gjc/skills/team/SKILL.md +7 -9
  64. package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
  65. package/src/eval/py/index.ts +2 -2
  66. package/src/export/html/template.generated.ts +1 -1
  67. package/src/extensibility/plugins/marketplace/manager.ts +94 -10
  68. package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
  69. package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
  70. package/src/gjc-runtime/deep-interview-state.ts +127 -3
  71. package/src/gjc-runtime/launch-tmux.ts +61 -6
  72. package/src/gjc-runtime/managed-owner-admission.ts +352 -0
  73. package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
  74. package/src/gjc-runtime/state-runtime.ts +38 -12
  75. package/src/gjc-runtime/team-runtime.ts +1 -1
  76. package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
  77. package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
  78. package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
  79. package/src/harness-control-plane/owner.ts +59 -3
  80. package/src/hindsight/client.ts +65 -2
  81. package/src/internal-urls/agent-protocol.ts +76 -5
  82. package/src/internal-urls/docs-index.generated.ts +8 -7
  83. package/src/internal-urls/local-protocol.ts +416 -18
  84. package/src/lsp/index.ts +4 -88
  85. package/src/main.ts +36 -12
  86. package/src/modes/acp/acp-agent.ts +8 -0
  87. package/src/modes/components/ansi-display-validator.ts +54 -0
  88. package/src/modes/components/notifications-settings-editor.ts +26 -2
  89. package/src/modes/components/session-observer-overlay.ts +40 -24
  90. package/src/modes/components/settings-selector.ts +3 -1
  91. package/src/modes/components/tool-transcript-format.ts +321 -0
  92. package/src/modes/components/transcript-viewer-overlay.ts +39 -17
  93. package/src/modes/controllers/command-controller.ts +15 -0
  94. package/src/modes/controllers/extension-ui-controller.ts +14 -2
  95. package/src/modes/controllers/goal-mode-controller.ts +20 -1
  96. package/src/modes/controllers/plan-mode-controller.ts +2 -0
  97. package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
  98. package/src/modes/controllers/selector-controller.ts +70 -8
  99. package/src/modes/interactive-mode.ts +1 -0
  100. package/src/modes/runtime-init.ts +16 -2
  101. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
  102. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
  103. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
  104. package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
  105. package/src/plan-mode/approved-plan.ts +3 -1
  106. package/src/prompts/agents/architect.md +1 -0
  107. package/src/prompts/agents/critic.md +1 -0
  108. package/src/prompts/system/custom-system-prompt.md +1 -1
  109. package/src/prompts/system/system-prompt.md +2 -1
  110. package/src/prompts/tools/lsp.md +3 -3
  111. package/src/runtime-mcp/manager.ts +16 -1
  112. package/src/runtime-mcp/oauth-discovery.ts +116 -26
  113. package/src/sdk/broker/session-index.ts +122 -64
  114. package/src/sdk/bus/chat-daemon-cli.ts +72 -10
  115. package/src/sdk/bus/chat-daemon-control.ts +538 -130
  116. package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
  117. package/src/sdk/bus/config-commands.ts +22 -0
  118. package/src/sdk/bus/config.ts +37 -0
  119. package/src/sdk/bus/index.ts +388 -94
  120. package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
  121. package/src/sdk/bus/notification-service.ts +284 -62
  122. package/src/sdk/bus/recent-activity.ts +14 -5
  123. package/src/sdk/bus/session-control.ts +150 -44
  124. package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
  125. package/src/sdk/bus/telegram-daemon-contract.ts +11 -3
  126. package/src/sdk/bus/telegram-daemon-control.ts +312 -76
  127. package/src/sdk/bus/telegram-daemon.ts +1916 -285
  128. package/src/sdk/host/control/dispatch.ts +14 -5
  129. package/src/sdk/protocol/operation-inventory.generated.json +29 -0
  130. package/src/sdk/session.ts +155 -27
  131. package/src/secrets/index.ts +53 -10
  132. package/src/secrets/obfuscator.ts +21 -18
  133. package/src/secrets/regex.ts +2 -0
  134. package/src/session/agent-session.ts +1447 -852
  135. package/src/session/artifacts.ts +188 -23
  136. package/src/session/context-estimation.ts +7 -2
  137. package/src/session/internal/managed-session-scope.ts +200 -38
  138. package/src/session/internal/managed-session-storage.ts +1152 -46
  139. package/src/session/session-manager.ts +1121 -148
  140. package/src/session/session-storage.ts +109 -11
  141. package/src/session/streaming-output.ts +6 -0
  142. package/src/session/yield-queue.ts +20 -0
  143. package/src/slash-commands/builtin-registry.ts +36 -0
  144. package/src/task/executor.ts +71 -26
  145. package/src/task/index.ts +63 -20
  146. package/src/tool-discovery/tool-index.ts +18 -0
  147. package/src/tools/ask-answer-registry.ts +15 -1
  148. package/src/tools/ask.ts +129 -32
  149. package/src/tools/bash-skill-urls.ts +14 -3
  150. package/src/tools/bash.ts +2 -2
  151. package/src/tools/index.ts +4 -0
  152. package/src/tools/plan-mode-guard.ts +1 -0
  153. package/src/tools/sqlite-reader.ts +3 -2
  154. package/src/tools/subagent.ts +2 -0
package/src/main.ts CHANGED
@@ -36,6 +36,8 @@ import { BUNDLED_GROK_BUILD_EXTENSION_ID, getBundledGrokBuildExtensionFactory }
36
36
  import { initializeWithSettings } from "./discovery";
37
37
  import { exportFromFile } from "./export/html";
38
38
  import type { ExtensionUIContext } from "./extensibility/extensions/types";
39
+ import { admitManagedOwnerBeforeCli, completeManagedOwnerRecovery } from "./gjc-runtime/managed-owner-admission";
40
+ import { isManagedOwnerSupervisorArgv, runManagedOwnerSupervisor } from "./gjc-runtime/managed-owner-supervisor";
39
41
  import { persistCoordinatorRuntimeInputReady } from "./gjc-runtime/session-state-sidecar";
40
42
  import { isTmuxOwnerIsolationCliArgv, runTmuxOwnerIsolationCliFromStdin } from "./gjc-runtime/tmux-owner-isolation-cli";
41
43
  import type { AcpStartupOptions } from "./modes/acp/startup-options";
@@ -58,6 +60,7 @@ import type { AgentSession } from "./session/agent-session";
58
60
  import {
59
61
  type ResumeSessionIdentity,
60
62
  resolveResumableSession,
63
+ type SessionDestination,
61
64
  type SessionDirectoryMigrationPolicy,
62
65
  type SessionInfo,
63
66
  SessionManager,
@@ -535,7 +538,8 @@ type ListForResumePickerReadOnly = (cwd: string, sessionDir?: string) => Promise
535
538
  type SelectResumeSession = (sessions: SessionInfo[]) => Promise<SessionSelectionResult>;
536
539
  type OpenExistingSessionStrict = (
537
540
  identity: ResumeSessionIdentity,
538
- sessionDir?: string,
541
+ destination: SessionDestination,
542
+ storage?: undefined,
539
543
  migrationPolicy?: SessionDirectoryMigrationPolicy,
540
544
  ) => Promise<StrictSessionOpenResult>;
541
545
 
@@ -722,6 +726,10 @@ export async function createSessionManager(
722
726
  activeSettings: Settings = settings,
723
727
  ): Promise<SessionManager | undefined> {
724
728
  const migrationPolicy = activeSettings.get("session.directoryMigration") === "disabled" ? "disabled" : "copy-retain";
729
+ const sessionDestination = () =>
730
+ parsed.sessionDir
731
+ ? SessionManager.explicitDestination(parsed.sessionDir)
732
+ : SessionManager.managedDestination(cwd, activeSettings.getAgentDir());
725
733
  if (parsed.resume === true) {
726
734
  return undefined;
727
735
  }
@@ -731,13 +739,13 @@ export async function createSessionManager(
731
739
  }
732
740
  const forkSource = parsed.fork;
733
741
  if (forkSource.includes("/") || forkSource.includes("\\") || forkSource.endsWith(".jsonl")) {
734
- return await SessionManager.forkFrom(forkSource, cwd, parsed.sessionDir, undefined, migrationPolicy);
742
+ return await SessionManager.forkFrom(forkSource, cwd, sessionDestination(), undefined, migrationPolicy);
735
743
  }
736
744
  const match = await resolveResumableSession(forkSource, cwd, parsed.sessionDir);
737
745
  if (!match) {
738
746
  throw new Error(`Session "${forkSource}" not found.`);
739
747
  }
740
- return await SessionManager.forkFrom(match.session.path, cwd, parsed.sessionDir, undefined, migrationPolicy);
748
+ return await SessionManager.forkFrom(match.session.path, cwd, sessionDestination(), undefined, migrationPolicy);
741
749
  }
742
750
 
743
751
  if (parsed.noSession) {
@@ -746,7 +754,12 @@ export async function createSessionManager(
746
754
  if (typeof parsed.resume === "string") {
747
755
  const sessionArg = parsed.resume;
748
756
  if (sessionArg.includes("/") || sessionArg.includes("\\") || sessionArg.endsWith(".jsonl")) {
749
- return await SessionManager.open(sessionArg, parsed.sessionDir, undefined, migrationPolicy);
757
+ return await SessionManager.open(
758
+ sessionArg,
759
+ SessionManager.explicitDestination(path.dirname(sessionArg)),
760
+ undefined,
761
+ migrationPolicy,
762
+ );
750
763
  }
751
764
  const match = await resolveResumableSession(sessionArg, cwd, parsed.sessionDir);
752
765
  if (!match) {
@@ -763,21 +776,21 @@ export async function createSessionManager(
763
776
  return await SessionManager.forkFrom(
764
777
  match.session.path,
765
778
  cwd,
766
- parsed.sessionDir,
779
+ sessionDestination(),
767
780
  undefined,
768
781
  migrationPolicy,
769
782
  );
770
783
  }
771
784
  }
772
- return await SessionManager.open(match.session.path, parsed.sessionDir, undefined, migrationPolicy);
785
+ return await SessionManager.open(match.session.path, sessionDestination(), undefined, migrationPolicy);
773
786
  }
774
787
  if (parsed.continue) {
775
- return await SessionManager.continueRecent(cwd, parsed.sessionDir, undefined, migrationPolicy);
788
+ return await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
776
789
  }
777
790
  // --resume without value is handled separately (needs picker UI)
778
791
  // If --session-dir provided without --continue/--resume, create new session there
779
792
  if (parsed.sessionDir) {
780
- return SessionManager.create(cwd, parsed.sessionDir);
793
+ return SessionManager.create(cwd, SessionManager.explicitDestination(parsed.sessionDir));
781
794
  }
782
795
  // A lifecycle `/session_create` child must start a FRESH session that adopts
783
796
  // the pre-allocated id (GJC_SESSION_ID), never auto-resume existing history in
@@ -795,14 +808,13 @@ export async function createSessionManager(
795
808
  // buildSessionOptions restores the session's model/thinking instead of
796
809
  // overriding them with CLI defaults.
797
810
  if (activeSettings.get("autoResume")) {
798
- const manager = await SessionManager.continueRecent(cwd, parsed.sessionDir, undefined, migrationPolicy);
811
+ const manager = await SessionManager.continueRecent(cwd, sessionDestination(), undefined, migrationPolicy);
799
812
  if (manager.getEntries().length > 0) {
800
813
  parsed.continue = true;
801
814
  }
802
815
  return manager;
803
816
  }
804
- const sessionDir = parsed.sessionDir ?? SessionManager.getDefaultSessionDir(cwd, activeSettings.getAgentDir());
805
- return SessionManager.create(cwd, sessionDir);
817
+ return SessionManager.create(cwd, SessionManager.managedDestination(cwd, activeSettings.getAgentDir()));
806
818
  }
807
819
 
808
820
  async function maybeAutoChdir(parsed: Args): Promise<void> {
@@ -1106,7 +1118,9 @@ export async function runRootCommand(
1106
1118
  try {
1107
1119
  opened = await (deps.openExistingSessionStrict ?? SessionManager.openExistingStrict)(
1108
1120
  selection.identity,
1109
- parsedArgs.sessionDir,
1121
+ parsedArgs.sessionDir
1122
+ ? SessionManager.explicitDestination(parsedArgs.sessionDir)
1123
+ : SessionManager.managedDestination(resumeCwd, settings.getAgentDir()),
1110
1124
  undefined,
1111
1125
  resumeMigrationPolicy,
1112
1126
  );
@@ -1604,6 +1618,16 @@ export async function main(args: string[]): Promise<void> {
1604
1618
  await runTmuxOwnerIsolationCliFromStdin();
1605
1619
  return;
1606
1620
  }
1621
+ if (isManagedOwnerSupervisorArgv(args)) {
1622
+ await runManagedOwnerSupervisor();
1623
+ return;
1624
+ }
1625
+ const admission = await admitManagedOwnerBeforeCli();
1626
+ if (admission.kind === "blocked") return;
1627
+ if (admission.kind === "recovery") {
1628
+ await completeManagedOwnerRecovery(admission.context);
1629
+ return;
1630
+ }
1607
1631
  const { runCli } = await import("./cli");
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;