t3code-cli 0.14.1 → 0.14.2

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 (162) hide show
  1. package/README.md +1 -0
  2. package/dist/application.d.ts +2 -2
  3. package/dist/application.js +1 -1
  4. package/dist/auth.d.ts +1 -1
  5. package/dist/auth.js +1 -1
  6. package/dist/bin.js +544 -262
  7. package/dist/cli.d.ts +1 -1
  8. package/dist/cli.js +1 -1
  9. package/dist/client-runtime/authorization.d.ts +1 -1
  10. package/dist/client-runtime/authorization.js +1 -1
  11. package/dist/client-runtime/connection.d.ts +1 -1
  12. package/dist/client-runtime/connection.js +1 -1
  13. package/dist/client-runtime/environment.d.ts +5 -1
  14. package/dist/client-runtime/environment.js +1 -1
  15. package/dist/client-runtime/errors.js +1 -1
  16. package/dist/client-runtime/operations/projects.d.ts +2 -2
  17. package/dist/client-runtime/operations/projects.js +2 -2
  18. package/dist/client-runtime/operations.d.ts +2 -2
  19. package/dist/client-runtime/operations.js +2 -2
  20. package/dist/client-runtime/platform.d.ts +1 -1
  21. package/dist/client-runtime/platform.js +1 -1
  22. package/dist/client-runtime/relay.d.ts +5 -1
  23. package/dist/client-runtime/relay.js +1 -1
  24. package/dist/client-runtime/rpc.d.ts +2 -2
  25. package/dist/client-runtime/rpc.js +2 -2
  26. package/dist/client-runtime/state/assets.d.ts +17 -11
  27. package/dist/client-runtime/state/assets.js +1 -1
  28. package/dist/client-runtime/state/auth.d.ts +1 -1
  29. package/dist/client-runtime/state/auth.js +1 -1
  30. package/dist/client-runtime/state/connections.js +1 -1
  31. package/dist/client-runtime/state/entities.d.ts +1 -1
  32. package/dist/client-runtime/state/entities.js +1 -1
  33. package/dist/client-runtime/state/filesystem.d.ts +1 -1
  34. package/dist/client-runtime/state/filesystem.js +1 -1
  35. package/dist/client-runtime/state/git.d.ts +5 -5
  36. package/dist/client-runtime/state/git.js +1 -1
  37. package/dist/client-runtime/state/models.d.ts +1 -1
  38. package/dist/client-runtime/state/models.js +1 -1
  39. package/dist/client-runtime/state/orchestration.d.ts +42 -2
  40. package/dist/client-runtime/state/orchestration.js +13 -1
  41. package/dist/client-runtime/state/presentation.d.ts +9 -3
  42. package/dist/client-runtime/state/presentation.js +1 -1
  43. package/dist/client-runtime/state/preview.d.ts +55 -48
  44. package/dist/client-runtime/state/preview.js +1 -1
  45. package/dist/client-runtime/state/project-grouping.d.ts +26 -2
  46. package/dist/client-runtime/state/project-grouping.js +83 -3
  47. package/dist/client-runtime/state/projects.d.ts +24 -18
  48. package/dist/client-runtime/state/projects.js +1 -1
  49. package/dist/client-runtime/state/relay.d.ts +1 -1
  50. package/dist/client-runtime/state/relay.js +1 -1
  51. package/dist/client-runtime/state/review.d.ts +16 -1
  52. package/dist/client-runtime/state/review.js +27 -6
  53. package/dist/client-runtime/state/runtime.d.ts +2 -2
  54. package/dist/client-runtime/state/runtime.js +2 -2
  55. package/dist/client-runtime/state/server.d.ts +299 -172
  56. package/dist/client-runtime/state/server.js +192 -16
  57. package/dist/client-runtime/state/session.d.ts +65 -7
  58. package/dist/client-runtime/state/session.js +32 -3
  59. package/dist/client-runtime/state/shell.d.ts +41 -7
  60. package/dist/client-runtime/state/shell.js +1 -1
  61. package/dist/client-runtime/state/source-control.d.ts +2 -2
  62. package/dist/client-runtime/state/source-control.js +1 -1
  63. package/dist/client-runtime/state/subagentRuntime.d.ts +145 -0
  64. package/dist/client-runtime/state/subagentRuntime.js +521 -0
  65. package/dist/client-runtime/state/terminal.d.ts +17 -17
  66. package/dist/client-runtime/state/terminal.js +1 -1
  67. package/dist/client-runtime/state/thread-search.d.ts +23 -0
  68. package/dist/client-runtime/state/thread-search.js +38 -0
  69. package/dist/client-runtime/state/thread-settled.d.ts +28 -3
  70. package/dist/client-runtime/state/thread-settled.js +74 -2
  71. package/dist/client-runtime/state/thread-sort.d.ts +59 -2
  72. package/dist/client-runtime/state/thread-sort.js +128 -2
  73. package/dist/client-runtime/state/threads.d.ts +115 -3
  74. package/dist/client-runtime/state/threads.js +309 -28
  75. package/dist/client-runtime/state/vcs.d.ts +14 -14
  76. package/dist/client-runtime/state/vcs.js +1 -1
  77. package/dist/config.d.ts +1 -1
  78. package/dist/config.js +1 -1
  79. package/dist/connection.d.ts +1 -1
  80. package/dist/connection.js +1 -1
  81. package/dist/contracts.d.ts +1 -1
  82. package/dist/contracts.js +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.js +1 -1
  85. package/dist/node.d.ts +1 -1
  86. package/dist/node.js +1 -1
  87. package/dist/orchestration.d.ts +144 -1
  88. package/dist/orchestration.js +1 -1
  89. package/dist/preview.d.ts +1 -1
  90. package/dist/preview.js +1 -1
  91. package/dist/rpc.d.ts +883 -351
  92. package/dist/rpc.js +1 -1
  93. package/dist/runtime.d.ts +1 -1
  94. package/dist/runtime.js +1 -1
  95. package/dist/shared/DrainableWorker.js +1 -1
  96. package/dist/shared/KeyedCoalescingWorker.js +1 -1
  97. package/dist/shared/Net.js +1 -1
  98. package/dist/shared/advertisedEndpoint.d.ts +1 -1
  99. package/dist/shared/advertisedEndpoint.js +1 -1
  100. package/dist/shared/agentAwareness.d.ts +1 -1
  101. package/dist/shared/backgroundActivitySettings.d.ts +1 -1
  102. package/dist/shared/composerInlineTokens.js +1 -1
  103. package/dist/shared/connectAuth.js +1 -1
  104. package/dist/shared/devHome.js +1 -1
  105. package/dist/shared/dpop.js +1 -1
  106. package/dist/shared/git.d.ts +1 -1
  107. package/dist/shared/httpObservability.js +1 -1
  108. package/dist/shared/httpReadiness.js +1 -1
  109. package/dist/shared/keybindings.d.ts +2 -2
  110. package/dist/shared/keybindings.js +15 -1
  111. package/dist/shared/model.d.ts +1 -1
  112. package/dist/shared/oauthScope.js +1 -1
  113. package/dist/shared/observability.d.ts +8 -1
  114. package/dist/shared/observability.js +49 -5
  115. package/dist/shared/path.js +1 -1
  116. package/dist/shared/preview.js +1 -1
  117. package/dist/shared/previewViewport.d.ts +1 -1
  118. package/dist/shared/previewViewport.js +1 -1
  119. package/dist/shared/projectFavicon.d.ts +2 -1
  120. package/dist/shared/projectFavicon.js +13 -1
  121. package/dist/shared/projectScripts.d.ts +1 -1
  122. package/dist/shared/relayAuth.js +1 -1
  123. package/dist/shared/relayClient.d.ts +1 -1
  124. package/dist/shared/relayClient.js +1 -1
  125. package/dist/shared/relayJwt.js +1 -1
  126. package/dist/shared/relayTracing.js +1 -1
  127. package/dist/shared/relayUrl.js +1 -1
  128. package/dist/shared/remote.js +1 -1
  129. package/dist/shared/schemaYaml.js +3 -3
  130. package/dist/shared/semver.js +1 -1
  131. package/dist/shared/serverSettings.d.ts +1 -1
  132. package/dist/shared/serverSettings.js +1 -1
  133. package/dist/shared/shell.d.ts +6 -1
  134. package/dist/shared/shell.js +42 -4
  135. package/dist/shared/sourceControl.d.ts +1 -1
  136. package/dist/shared/t3ProjectFile.d.ts +9 -1
  137. package/dist/shared/t3ProjectFile.js +12 -2
  138. package/dist/shared/terminalLabels.d.ts +1 -1
  139. package/dist/shared/threadEnvMode.d.ts +29 -0
  140. package/dist/shared/threadEnvMode.js +24 -0
  141. package/dist/shared/toolActivity.d.ts +1 -1
  142. package/dist/shared/usageFormat.d.ts +21 -0
  143. package/dist/shared/usageFormat.js +90 -0
  144. package/dist/shared/usageMerge.d.ts +67 -0
  145. package/dist/shared/usageMerge.js +218 -0
  146. package/dist/shared.d.ts +4472 -668
  147. package/dist/shared.js +16867 -16273
  148. package/dist/t3tools.d.ts +2 -2
  149. package/dist/t3tools.js +2 -2
  150. package/package.json +5 -5
  151. package/src/application/index.ts +1 -0
  152. package/src/application/service.ts +5 -1
  153. package/src/application/threads.ts +43 -1
  154. package/src/cli/app.ts +2 -0
  155. package/src/cli/format/thread.ts +23 -1
  156. package/src/cli/search.ts +48 -0
  157. package/src/config/persist/schema.ts +1 -1
  158. package/src/orchestration/layer.ts +6 -0
  159. package/src/orchestration/service.ts +5 -0
  160. package/src/rpc/error.ts +2 -0
  161. package/src/rpc/operation.ts +2 -0
  162. package/src/rpc/ws-group.ts +2 -0
@@ -0,0 +1,521 @@
1
+ //#region upstream-t3code/packages/client-runtime/src/state/subagentRuntime.ts
2
+ const TERMINAL_STATUSES = /* @__PURE__ */ new Set([
3
+ "completed",
4
+ "failed",
5
+ "cancelled",
6
+ "interrupted"
7
+ ]);
8
+ function isTerminalSubagentStatus(status) {
9
+ return TERMINAL_STATUSES.has(status);
10
+ }
11
+ /** Active = the user may still need to care while it runs. Idle is settled-ish
12
+ * but resumable; waiting counts as active because it needs the user. */
13
+ function isActiveSubagentStatus(status) {
14
+ return status === "pending" || status === "running" || status === "waiting";
15
+ }
16
+ const RECENT_ACTIVITY_LIMIT = 6;
17
+ const SUMMARY_CHAR_LIMIT = 180;
18
+ const ROSTER_LIMIT = 100;
19
+ /**
20
+ * True when this activity's payload does NOT belong on the Agents surface.
21
+ * Classification happens exactly once, server-side at ingestion
22
+ * (classifyTaskAgentKind → the persisted agentKind stamp); the client only
23
+ * reads it. Rows without a stamp — legacy threads, pre-stamp servers — are
24
+ * background by definition: they render in the ordinary work log, exactly
25
+ * as they did before this feature existed.
26
+ */
27
+ function isBackgroundTaskActivity(payload) {
28
+ return payload.agentKind !== "agent";
29
+ }
30
+ function bounded(value) {
31
+ return value.length <= SUMMARY_CHAR_LIMIT ? value : `${value.slice(0, SUMMARY_CHAR_LIMIT - 1)}…`;
32
+ }
33
+ /** Appends to the ring buffer, deduping consecutive identical summaries. */
34
+ function appendActivity(entries, at, summary) {
35
+ const boundedSummary = bounded(summary);
36
+ if (entries.length > 0 && entries[entries.length - 1]?.summary === boundedSummary) return entries;
37
+ const next = [...entries, {
38
+ at,
39
+ summary: boundedSummary
40
+ }];
41
+ return next.length > RECENT_ACTIVITY_LIMIT ? next.slice(-6) : next;
42
+ }
43
+ function asString(value) {
44
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
45
+ }
46
+ function asCount(value) {
47
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
48
+ }
49
+ function asUsage(value) {
50
+ if (typeof value !== "object" || value === null) return;
51
+ const record = value;
52
+ const totalTokens = asCount(record.totalTokens);
53
+ if (totalTokens === void 0) return;
54
+ const usage = { totalTokens };
55
+ const inputTokens = asCount(record.inputTokens);
56
+ if (inputTokens !== void 0) usage.inputTokens = inputTokens;
57
+ const cachedInputTokens = asCount(record.cachedInputTokens);
58
+ if (cachedInputTokens !== void 0) usage.cachedInputTokens = cachedInputTokens;
59
+ const outputTokens = asCount(record.outputTokens);
60
+ if (outputTokens !== void 0) usage.outputTokens = outputTokens;
61
+ const reasoningOutputTokens = asCount(record.reasoningOutputTokens);
62
+ if (reasoningOutputTokens !== void 0) usage.reasoningOutputTokens = reasoningOutputTokens;
63
+ const toolUses = asCount(record.toolUses);
64
+ if (toolUses !== void 0) usage.toolUses = toolUses;
65
+ const durationMs = asCount(record.durationMs);
66
+ if (durationMs !== void 0) usage.durationMs = durationMs;
67
+ return usage;
68
+ }
69
+ /**
70
+ * Provider-specific usage merge (#4779 semantics, verbatim):
71
+ * - max-merge (Codex-style cumulative frames): field-wise maximum, idempotent
72
+ * under duplicate or late frames. Cumulative totals never shrink.
73
+ * - accumulate (Claude-style activation deltas): not needed at this layer —
74
+ * Claude's task_progress usage is itself cumulative per task, so the fold
75
+ * also max-merges. The distinction matters when v2 sums activations.
76
+ * Field-wise: a terminal payload carrying only totalTokens must not wipe a
77
+ * known breakdown.
78
+ */
79
+ function mergeUsageMax(current, incoming) {
80
+ if (!incoming) return current;
81
+ if (!current) return incoming;
82
+ const pick = (a, b) => a === void 0 ? b : b === void 0 ? a : Math.max(a, b);
83
+ const merged = { totalTokens: Math.max(current.totalTokens, incoming.totalTokens) };
84
+ const inputTokens = pick(current.inputTokens, incoming.inputTokens);
85
+ if (inputTokens !== void 0) merged.inputTokens = inputTokens;
86
+ const cachedInputTokens = pick(current.cachedInputTokens, incoming.cachedInputTokens);
87
+ if (cachedInputTokens !== void 0) merged.cachedInputTokens = cachedInputTokens;
88
+ const outputTokens = pick(current.outputTokens, incoming.outputTokens);
89
+ if (outputTokens !== void 0) merged.outputTokens = outputTokens;
90
+ const reasoningOutputTokens = pick(current.reasoningOutputTokens, incoming.reasoningOutputTokens);
91
+ if (reasoningOutputTokens !== void 0) merged.reasoningOutputTokens = reasoningOutputTokens;
92
+ const toolUses = pick(current.toolUses, incoming.toolUses);
93
+ if (toolUses !== void 0) merged.toolUses = toolUses;
94
+ const durationMs = pick(current.durationMs, incoming.durationMs);
95
+ if (durationMs !== void 0) merged.durationMs = durationMs;
96
+ return merged;
97
+ }
98
+ function kindFromPayload(payload, agentId) {
99
+ if (asString(payload.taskType) === "local_workflow") return "workflow";
100
+ if (payload.parentAgentId !== void 0 || agentId.includes(":wf:")) return "workflow_agent";
101
+ return "subagent";
102
+ }
103
+ /** Completion can create an agent (its start may have aged out of retention). */
104
+ function getOrCreate(agents, id, payload, at) {
105
+ const existing = agents.get(id);
106
+ if (existing) return existing;
107
+ const created = {
108
+ id,
109
+ kind: kindFromPayload(payload, id),
110
+ title: asString(payload.title) ?? asString(payload.detail) ?? id,
111
+ role: asString(payload.role) ?? null,
112
+ model: asString(payload.model) ?? null,
113
+ effort: asString(payload.effort) ?? null,
114
+ status: "pending",
115
+ activationCount: 0,
116
+ usage: null,
117
+ progress: null,
118
+ lastToolName: null,
119
+ result: null,
120
+ error: null,
121
+ outputFile: null,
122
+ parentAgentId: asString(payload.parentAgentId) ?? null,
123
+ agentIndex: asCount(payload.agentIndex) ?? null,
124
+ phaseIndex: asCount(payload.phaseIndex) ?? null,
125
+ phaseTitle: asString(payload.phaseTitle) ?? null,
126
+ attempt: asCount(payload.attempt) ?? null,
127
+ workflowName: asString(payload.workflowName) ?? null,
128
+ phases: [],
129
+ runHandles: null,
130
+ recentActivity: [],
131
+ firstSeenAt: at,
132
+ startedAt: null,
133
+ completedAt: null,
134
+ updatedAt: at
135
+ };
136
+ agents.set(id, created);
137
+ return created;
138
+ }
139
+ /** Metadata fill from any payload: never downgrades known values to null. */
140
+ function fillMetadata(agent, payload) {
141
+ const title = asString(payload.title);
142
+ if (title) agent.title = title;
143
+ const role = asString(payload.role);
144
+ if (role) agent.role = role;
145
+ const model = asString(payload.model);
146
+ if (model) agent.model = model;
147
+ const effort = asString(payload.effort);
148
+ if (effort) agent.effort = effort;
149
+ const parentAgentId = asString(payload.parentAgentId);
150
+ if (parentAgentId) {
151
+ agent.parentAgentId = parentAgentId;
152
+ if (agent.kind === "subagent") agent.kind = "workflow_agent";
153
+ }
154
+ const workflowName = asString(payload.workflowName);
155
+ if (workflowName) agent.workflowName = workflowName;
156
+ if (asString(payload.taskType) === "local_workflow") agent.kind = "workflow";
157
+ const agentIndex = asCount(payload.agentIndex);
158
+ if (agentIndex !== void 0) agent.agentIndex = agentIndex;
159
+ const phaseIndex = asCount(payload.phaseIndex);
160
+ if (phaseIndex !== void 0) agent.phaseIndex = phaseIndex;
161
+ const phaseTitle = asString(payload.phaseTitle);
162
+ if (phaseTitle) agent.phaseTitle = phaseTitle;
163
+ const attempt = asCount(payload.attempt);
164
+ if (attempt !== void 0) {
165
+ if (agent.attempt !== null && attempt > agent.attempt) {
166
+ agent.result = null;
167
+ agent.error = null;
168
+ agent.completedAt = null;
169
+ }
170
+ agent.attempt = attempt;
171
+ }
172
+ const outputFile = asString(payload.outputFile);
173
+ if (outputFile) agent.outputFile = outputFile;
174
+ if (Array.isArray(payload.phases)) {
175
+ const phases = [];
176
+ for (const entry of payload.phases) {
177
+ if (typeof entry !== "object" || entry === null) continue;
178
+ const record = entry;
179
+ const index = asCount(record.index);
180
+ const phaseName = asString(record.title);
181
+ if (index !== void 0 && phaseName) phases.push({
182
+ index,
183
+ title: phaseName
184
+ });
185
+ }
186
+ if (phases.length > 0) agent.phases = phases.slice().sort((a, b) => a.index - b.index);
187
+ }
188
+ if (typeof payload.runHandles === "object" && payload.runHandles !== null) {
189
+ const record = payload.runHandles;
190
+ const runHandles = {};
191
+ const runId = asString(record.runId);
192
+ if (runId) runHandles.runId = runId;
193
+ const scriptPath = asString(record.scriptPath);
194
+ if (scriptPath) runHandles.scriptPath = scriptPath;
195
+ const transcriptDir = asString(record.transcriptDir);
196
+ if (transcriptDir) runHandles.transcriptDir = transcriptDir;
197
+ const sessionUrl = asString(record.sessionUrl);
198
+ if (sessionUrl && /^https?:\/\//i.test(sessionUrl)) runHandles.sessionUrl = sessionUrl;
199
+ if (Object.keys(runHandles).length > 0) agent.runHandles = {
200
+ ...agent.runHandles,
201
+ ...runHandles
202
+ };
203
+ }
204
+ }
205
+ function applyStatus(agent, status, at) {
206
+ const wasTerminal = isTerminalSubagentStatus(agent.status);
207
+ const isTerminal = isTerminalSubagentStatus(status);
208
+ if (wasTerminal && isTerminal) return;
209
+ if ((wasTerminal || agent.status === "idle") && (status === "running" || status === "pending")) {
210
+ agent.activationCount += 1;
211
+ agent.result = null;
212
+ agent.error = null;
213
+ agent.completedAt = null;
214
+ if (status === "running") agent.startedAt = at;
215
+ }
216
+ if (status === "running" && agent.startedAt === null) agent.startedAt = at;
217
+ if (isTerminal && agent.completedAt === null) agent.completedAt = at;
218
+ agent.status = status;
219
+ }
220
+ const TASK_COMPLETED_STATUS = /* @__PURE__ */ new Map([
221
+ ["completed", "completed"],
222
+ ["failed", "failed"],
223
+ ["stopped", "interrupted"]
224
+ ]);
225
+ const KNOWN_STATUSES = /* @__PURE__ */ new Set([
226
+ "pending",
227
+ "running",
228
+ "waiting",
229
+ "idle",
230
+ "completed",
231
+ "failed",
232
+ "cancelled",
233
+ "interrupted"
234
+ ]);
235
+ function asRuntimeStatus(value) {
236
+ return typeof value === "string" && KNOWN_STATUSES.has(value) ? value : void 0;
237
+ }
238
+ /**
239
+ * Folds a thread's persisted activities into subagent state. Tolerant by
240
+ * construction: malformed rows are skipped individually; unknown kinds are
241
+ * ignored. Pure — memoize by activity-list identity at the atom layer.
242
+ *
243
+ * sessionLive=false derives interruption: background tasks die with their
244
+ * provider session, so agents whose terminal rows were lost (server
245
+ * restart, crash) must not read as running forever (review finding: a dead
246
+ * session left a panel full of "Working" agents while the sidebar showed
247
+ * nothing). Idle is preserved — a resumable Codex child stays resumable.
248
+ */
249
+ function foldSubagentActivities(activities, options) {
250
+ const agents = /* @__PURE__ */ new Map();
251
+ for (const activity of activities) {
252
+ if (typeof activity.payload !== "object" || activity.payload === null) continue;
253
+ const payload = activity.payload;
254
+ const at = activity.createdAt;
255
+ switch (activity.kind) {
256
+ case "task.started": {
257
+ const taskId = asString(payload.taskId);
258
+ if (!taskId) break;
259
+ if (isBackgroundTaskActivity(payload)) break;
260
+ const agent = getOrCreate(agents, taskId, payload, at);
261
+ fillMetadata(agent, payload);
262
+ if (agent.activationCount === 0 && !isTerminalSubagentStatus(agent.status)) {
263
+ agent.activationCount = 1;
264
+ agent.startedAt = agent.startedAt ?? at;
265
+ agent.status = "running";
266
+ } else if (agent.status === "idle") applyStatus(agent, "running", at);
267
+ const detail = asString(payload.detail);
268
+ if (detail && agent.title === agent.id) agent.title = detail;
269
+ agent.updatedAt = at;
270
+ break;
271
+ }
272
+ case "task.progress": {
273
+ const taskId = asString(payload.taskId);
274
+ if (!taskId) break;
275
+ const existed = agents.has(taskId);
276
+ if (!existed && isBackgroundTaskActivity(payload)) break;
277
+ const agent = getOrCreate(agents, taskId, payload, at);
278
+ fillMetadata(agent, payload);
279
+ if (agent.activationCount === 0) agent.activationCount = 1;
280
+ const explicitStatus = asRuntimeStatus(payload.status);
281
+ if (explicitStatus) applyStatus(agent, explicitStatus, at);
282
+ else if ((payload.usageSnapshot !== true || !existed) && !isTerminalSubagentStatus(agent.status) && agent.status !== "idle") applyStatus(agent, "running", at);
283
+ const summary = asString(payload.summary);
284
+ if (summary) {
285
+ agent.progress = bounded(summary);
286
+ agent.recentActivity = appendActivity(agent.recentActivity, at, summary);
287
+ }
288
+ const lastToolName = asString(payload.lastToolName);
289
+ if (lastToolName) {
290
+ agent.lastToolName = lastToolName;
291
+ if (!summary) agent.recentActivity = appendActivity(agent.recentActivity, at, `▸ ${lastToolName}`);
292
+ }
293
+ const error = asString(payload.error);
294
+ if (error) agent.error = bounded(error);
295
+ agent.usage = mergeUsageMax(agent.usage, asUsage(payload.typedUsage));
296
+ agent.updatedAt = at;
297
+ break;
298
+ }
299
+ case "task.updated": {
300
+ const taskId = asString(payload.taskId);
301
+ if (!taskId) break;
302
+ if (!agents.has(taskId) && isBackgroundTaskActivity(payload)) break;
303
+ const agent = getOrCreate(agents, taskId, payload, at);
304
+ fillMetadata(agent, payload);
305
+ if (agent.activationCount === 0) agent.activationCount = 1;
306
+ const wasTerminal = isTerminalSubagentStatus(agent.status);
307
+ const status = asRuntimeStatus(payload.status);
308
+ if (status) applyStatus(agent, status, at);
309
+ const error = asString(payload.error);
310
+ if (error) agent.error = bounded(error);
311
+ const endedAt = asString(payload.endedAt);
312
+ if (endedAt && !wasTerminal && isTerminalSubagentStatus(agent.status)) agent.completedAt = endedAt;
313
+ agent.updatedAt = at;
314
+ break;
315
+ }
316
+ case "task.completed": {
317
+ const taskId = asString(payload.taskId);
318
+ if (!taskId) break;
319
+ if (!agents.has(taskId) && isBackgroundTaskActivity(payload)) break;
320
+ const agent = getOrCreate(agents, taskId, payload, at);
321
+ fillMetadata(agent, payload);
322
+ if (agent.activationCount === 0) agent.activationCount = 1;
323
+ const summary = asString(payload.summary) ?? asString(payload.detail);
324
+ if (isTerminalSubagentStatus(agent.status)) {
325
+ if (summary) if (agent.status === "failed") agent.error = agent.error ?? bounded(summary);
326
+ else agent.result = agent.result ?? bounded(summary);
327
+ agent.usage = mergeUsageMax(agent.usage, asUsage(payload.typedUsage));
328
+ break;
329
+ }
330
+ const status = TASK_COMPLETED_STATUS.get(asString(payload.status) ?? "") ?? "completed";
331
+ applyStatus(agent, status, at);
332
+ if (summary) if (status === "failed") agent.error = agent.error ?? bounded(summary);
333
+ else agent.result = bounded(summary);
334
+ agent.usage = mergeUsageMax(agent.usage, asUsage(payload.typedUsage));
335
+ agent.updatedAt = at;
336
+ break;
337
+ }
338
+ case "tool.progress": {
339
+ const taskId = asString(payload.taskId);
340
+ if (!taskId) break;
341
+ const agent = agents.get(taskId);
342
+ if (!agent) break;
343
+ const toolName = asString(payload.toolName);
344
+ if (toolName) {
345
+ agent.lastToolName = toolName;
346
+ agent.recentActivity = appendActivity(agent.recentActivity, at, `▸ ${toolName}`);
347
+ }
348
+ agent.updatedAt = at;
349
+ break;
350
+ }
351
+ default: break;
352
+ }
353
+ }
354
+ for (const agent of agents.values()) {
355
+ if (agent.kind !== "workflow" || !isTerminalSubagentStatus(agent.status)) continue;
356
+ for (const member of agents.values()) {
357
+ if (member.parentAgentId !== agent.id) continue;
358
+ if (isTerminalSubagentStatus(member.status) || member.status === "idle") continue;
359
+ member.status = agent.status === "completed" ? "completed" : "interrupted";
360
+ member.completedAt = member.completedAt ?? agent.completedAt ?? agent.updatedAt;
361
+ member.updatedAt = agent.updatedAt;
362
+ }
363
+ }
364
+ if (options?.sessionLive === false) {
365
+ for (const agent of agents.values()) if (isActiveSubagentStatus(agent.status)) {
366
+ agent.status = "interrupted";
367
+ agent.completedAt = agent.completedAt ?? agent.updatedAt;
368
+ }
369
+ }
370
+ let roster = Array.from(agents.values());
371
+ if (roster.length > ROSTER_LIMIT) {
372
+ const rank = (agent) => isActiveSubagentStatus(agent.status) ? 0 : agent.status === "idle" ? 1 : 2;
373
+ roster = roster.slice().sort((a, b) => rank(a) - rank(b) || b.updatedAt.localeCompare(a.updatedAt)).slice(0, ROSTER_LIMIT);
374
+ }
375
+ return roster.map((agent) => ({ ...agent }));
376
+ }
377
+ const EMPTY_PANEL_MODEL = {
378
+ workflows: [],
379
+ directAgents: [],
380
+ runningCount: 0,
381
+ waitingCount: 0,
382
+ idleCount: 0,
383
+ settledCount: 0,
384
+ totalTokens: 0,
385
+ hasAgents: false,
386
+ liveCount: 0
387
+ };
388
+ function emptyAgentPanelModel() {
389
+ return EMPTY_PANEL_MODEL;
390
+ }
391
+ /**
392
+ * Source-neutral view model. When the orchestration-v2 subagent projection
393
+ * exists for the thread, pass it as v2Projection and it wins outright — the
394
+ * two sources are never merged (duplicate-agents failure mode). Until v2
395
+ * lands, callers pass null and the native fold output is used.
396
+ */
397
+ function deriveAgentPanelModel({ agents, v2Projection }) {
398
+ const source = v2Projection ?? agents;
399
+ if (source.length === 0) return EMPTY_PANEL_MODEL;
400
+ const workflows = source.filter((agent) => agent.kind === "workflow").slice().sort((a, b) => a.firstSeenAt.localeCompare(b.firstSeenAt) || a.id.localeCompare(b.id));
401
+ const workflowIds = new Set(workflows.map((workflow) => workflow.id));
402
+ const members = /* @__PURE__ */ new Map();
403
+ const direct = [];
404
+ for (const agent of source) {
405
+ if (agent.kind === "workflow") continue;
406
+ if (agent.parentAgentId !== null && workflowIds.has(agent.parentAgentId)) {
407
+ const list = members.get(agent.parentAgentId) ?? [];
408
+ list.push(agent);
409
+ members.set(agent.parentAgentId, list);
410
+ } else direct.push(agent);
411
+ }
412
+ const workflowGroups = workflows.map((workflow) => {
413
+ const workflowMembers = members.get(workflow.id) ?? [];
414
+ const knownPhases = workflow.phases.length > 0 ? workflow.phases : (() => {
415
+ const derived = /* @__PURE__ */ new Map();
416
+ for (const member of workflowMembers) if (member.phaseIndex !== null && !derived.has(member.phaseIndex)) derived.set(member.phaseIndex, member.phaseTitle ?? `Phase ${member.phaseIndex + 1}`);
417
+ return Array.from(derived.entries()).map(([index, title]) => ({
418
+ index,
419
+ title
420
+ })).slice().sort((a, b) => a.index - b.index);
421
+ })();
422
+ const knownPhaseIndices = new Set(knownPhases.map((phase) => phase.index));
423
+ return {
424
+ workflow,
425
+ phases: knownPhases.map((phase) => {
426
+ const phaseMembers = workflowMembers.filter((member) => member.phaseIndex === phase.index).slice().sort((a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0));
427
+ const activeCount = phaseMembers.filter((member) => isActiveSubagentStatus(member.status) || member.status === "idle").length;
428
+ const settledCount = phaseMembers.filter((member) => isTerminalSubagentStatus(member.status)).length;
429
+ const state = phaseMembers.length === 0 ? "pending" : activeCount > 0 ? "running" : settledCount === phaseMembers.length ? "done" : "pending";
430
+ return {
431
+ index: phase.index,
432
+ title: phase.title,
433
+ members: phaseMembers,
434
+ state,
435
+ activeCount,
436
+ settledCount
437
+ };
438
+ }),
439
+ unphasedMembers: workflowMembers.filter((member) => member.phaseIndex === null || !knownPhaseIndices.has(member.phaseIndex)).slice().sort((a, b) => (a.agentIndex ?? 0) - (b.agentIndex ?? 0))
440
+ };
441
+ });
442
+ let runningCount = 0;
443
+ let waitingCount = 0;
444
+ let idleCount = 0;
445
+ let settledCount = 0;
446
+ let totalTokens = 0;
447
+ for (const agent of source) {
448
+ if (agent.status === "running" || agent.status === "pending") runningCount += 1;
449
+ else if (agent.status === "waiting") waitingCount += 1;
450
+ else if (agent.status === "idle") idleCount += 1;
451
+ else settledCount += 1;
452
+ if (agent.kind !== "workflow" || (members.get(agent.id) ?? []).length === 0) totalTokens += agent.usage?.totalTokens ?? 0;
453
+ }
454
+ return {
455
+ workflows: workflowGroups,
456
+ directAgents: direct.slice().sort((a, b) => a.firstSeenAt.localeCompare(b.firstSeenAt) || a.id.localeCompare(b.id)),
457
+ runningCount,
458
+ waitingCount,
459
+ idleCount,
460
+ settledCount,
461
+ totalTokens,
462
+ hasAgents: true,
463
+ liveCount: runningCount + waitingCount
464
+ };
465
+ }
466
+ /**
467
+ * Members ordered by urgency for the capped inline workflow card: running and
468
+ * failed first, then waiting, then most recently updated.
469
+ */
470
+ function workflowCardMembers(group, limit) {
471
+ const all = [...group.phases.flatMap((phase) => phase.members), ...group.unphasedMembers];
472
+ const urgency = (agent) => {
473
+ if (agent.status === "failed") return 0;
474
+ if (agent.status === "running") return 1;
475
+ if (agent.status === "waiting") return 2;
476
+ return 3;
477
+ };
478
+ const ordered = all.slice().sort((a, b) => urgency(a) - urgency(b) || b.updatedAt.localeCompare(a.updatedAt));
479
+ return {
480
+ visible: ordered.slice(0, limit),
481
+ overflow: Math.max(0, ordered.length - limit)
482
+ };
483
+ }
484
+ /** Kinds the timeline should not render as generic rows (fold input only). */
485
+ function isSubagentActivityKind(kind) {
486
+ return kind === "task.started" || kind === "task.progress" || kind === "task.updated" || kind === "task.completed" || kind === "tool.progress";
487
+ }
488
+ /**
489
+ * Quiet-timeline guarantee: tool rows attributed to an owning agent belong in
490
+ * the Agents surface, not the parent chat. Unattributed rows must stay.
491
+ */
492
+ function isAgentAttributedToolActivity(activity) {
493
+ if (typeof activity.payload !== "object" || activity.payload === null) return false;
494
+ const payload = activity.payload;
495
+ return typeof payload.agentId === "string" && payload.agentId.trim().length > 0;
496
+ }
497
+ /** Timeline-bypassing synthesized rows (Codex children, workflow members). */
498
+ function isTimelineBypassActivity(activity) {
499
+ if (typeof activity.payload !== "object" || activity.payload === null) return false;
500
+ return activity.payload.timelineBypass === true;
501
+ }
502
+ /**
503
+ * Compact model chip text: strips vendor prefixes/date-or-context suffixes
504
+ * ("claude-sonnet-5[1m]" → "sonnet-5[1m]", "claude-opus-4-20250514" →
505
+ * "opus-4"). Unknown ids pass through untouched; effort appends as "· high".
506
+ */
507
+ function formatSubagentModelLabel(model, effort) {
508
+ if (!model) return null;
509
+ const compact = model.replace(/^claude-/, "").replace(/-\d{8}$/, "").replace(/-latest$/, "");
510
+ return effort ? `${compact} · ${effort}` : compact;
511
+ }
512
+ function formatSubagentTokenCount(totalTokens) {
513
+ if (totalTokens < 1e3) return `${totalTokens}`;
514
+ if (totalTokens < 1e6) {
515
+ const value = totalTokens / 1e3;
516
+ return `${value >= 100 ? Math.round(value) : value.toFixed(1)}k`;
517
+ }
518
+ return `${(totalTokens / 1e6).toFixed(1)}M`;
519
+ }
520
+ //#endregion
521
+ export { deriveAgentPanelModel, emptyAgentPanelModel, foldSubagentActivities, formatSubagentModelLabel, formatSubagentTokenCount, isActiveSubagentStatus, isAgentAttributedToolActivity, isBackgroundTaskActivity, isSubagentActivityKind, isTerminalSubagentStatus, isTimelineBypassActivity, workflowCardMembers };
@@ -1,4 +1,4 @@
1
- import { $T as ThreadId, Hb as TerminalCwdNotDirectoryError, Jb as TerminalMetadataStreamEvent, Rb as TerminalAttachStreamEvent, Ub as TerminalCwdNotFoundError, Wb as TerminalCwdStatError, Yb as TerminalNotRunningError, Zb as TerminalResizeError, bT as EnvironmentAuthorizationError, f as AtomCommand, ha as EnvironmentRegistry, ix as TerminalSummary, ma as EnvironmentNotRegisteredError, nx as TerminalSessionSnapshot, on as EnvironmentRpcUnavailableError, ox as TerminalWriteError, qb as TerminalHistoryError, tx as TerminalSessionLookupError, zT as EnvironmentId } from "../../shared.js";
1
+ import { AS as TerminalSessionLookupError, CD as EnvironmentId, CS as TerminalMetadataStreamEvent, ES as TerminalResizeError, FS as TerminalWriteError, NS as TerminalSummary, RD as ThreadId, SS as TerminalHistoryError, Ta as EnvironmentRegistry, _S as TerminalCwdNotDirectoryError, f as AtomCommand, iD as EnvironmentAuthorizationError, jS as TerminalSessionSnapshot, mn as EnvironmentRpcUnavailableError, pS as TerminalAttachStreamEvent, vS as TerminalCwdNotFoundError, wS as TerminalNotRunningError, wa as EnvironmentNotRegisteredError, yS as TerminalCwdStatError } from "../../shared.js";
2
2
  import { Atom } from "effect/unstable/reactivity";
3
3
  //#region upstream-t3code/packages/client-runtime/src/state/terminalSession.d.ts
4
4
  interface TerminalSessionState {
@@ -54,7 +54,7 @@ declare function createTerminalEnvironmentAtoms<R, E>(runtime: Atom.AtomRuntime<
54
54
  }) => Atom.Atom<import("effect/unstable/reactivity/AsyncResult").AsyncResult<TerminalBufferState, E | EnvironmentAuthorizationError | import("effect/Cause").NoSuchElementError | import("effect/unstable/rpc/RpcClientError").RpcClientError | TerminalCwdNotDirectoryError | TerminalCwdNotFoundError | TerminalCwdStatError | TerminalHistoryError | TerminalNotRunningError | TerminalResizeError | TerminalSessionLookupError | TerminalWriteError>>;
55
55
  events: (target: {
56
56
  readonly environmentId: EnvironmentId;
57
- readonly input: import("effect/Schema").Struct.ReadonlyMakeIn<{}>;
57
+ readonly input: {};
58
58
  }) => Atom.Atom<import("effect/unstable/reactivity/AsyncResult").AsyncResult<{
59
59
  readonly threadId: string;
60
60
  readonly terminalId: string;
@@ -175,27 +175,27 @@ declare function createTerminalEnvironmentAtoms<R, E>(runtime: Atom.AtomRuntime<
175
175
  }, E | EnvironmentAuthorizationError | EnvironmentNotRegisteredError | EnvironmentRpcUnavailableError | import("effect/unstable/rpc/RpcClientError").RpcClientError | TerminalCwdNotDirectoryError | TerminalCwdNotFoundError | TerminalCwdStatError | TerminalHistoryError | TerminalNotRunningError | TerminalResizeError | TerminalSessionLookupError | TerminalWriteError>;
176
176
  write: AtomCommand<{
177
177
  readonly environmentId: EnvironmentId;
178
- readonly input: import("effect/Schema").Struct.ReadonlyMakeIn<{
179
- readonly threadId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
180
- readonly terminalId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
181
- readonly data: import("effect/Schema").String;
182
- }>;
178
+ readonly input: {
179
+ readonly threadId: string;
180
+ readonly terminalId: string;
181
+ readonly data: string;
182
+ };
183
183
  }, void, E | EnvironmentAuthorizationError | EnvironmentNotRegisteredError | EnvironmentRpcUnavailableError | import("effect/unstable/rpc/RpcClientError").RpcClientError | TerminalCwdNotDirectoryError | TerminalCwdNotFoundError | TerminalCwdStatError | TerminalHistoryError | TerminalNotRunningError | TerminalResizeError | TerminalSessionLookupError | TerminalWriteError>;
184
184
  resize: AtomCommand<{
185
185
  readonly environmentId: EnvironmentId;
186
- readonly input: import("effect/Schema").Struct.ReadonlyMakeIn<{
187
- readonly threadId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
188
- readonly terminalId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
189
- readonly cols: import("effect/Schema").Int;
190
- readonly rows: import("effect/Schema").Int;
191
- }>;
186
+ readonly input: {
187
+ readonly threadId: string;
188
+ readonly terminalId: string;
189
+ readonly cols: number;
190
+ readonly rows: number;
191
+ };
192
192
  }, void, E | EnvironmentAuthorizationError | EnvironmentNotRegisteredError | EnvironmentRpcUnavailableError | import("effect/unstable/rpc/RpcClientError").RpcClientError | TerminalCwdNotDirectoryError | TerminalCwdNotFoundError | TerminalCwdStatError | TerminalHistoryError | TerminalNotRunningError | TerminalResizeError | TerminalSessionLookupError | TerminalWriteError>;
193
193
  clear: AtomCommand<{
194
194
  readonly environmentId: EnvironmentId;
195
- readonly input: import("effect/Schema").Struct.ReadonlyMakeIn<{
196
- readonly threadId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
197
- readonly terminalId: import("effect/Schema").decodeTo<import("effect/Schema").String, import("effect/Schema").String, never, never>;
198
- }>;
195
+ readonly input: {
196
+ readonly threadId: string;
197
+ readonly terminalId: string;
198
+ };
199
199
  }, void, E | EnvironmentAuthorizationError | EnvironmentNotRegisteredError | EnvironmentRpcUnavailableError | import("effect/unstable/rpc/RpcClientError").RpcClientError | TerminalCwdNotDirectoryError | TerminalCwdNotFoundError | TerminalCwdStatError | TerminalHistoryError | TerminalNotRunningError | TerminalResizeError | TerminalSessionLookupError | TerminalWriteError>;
200
200
  restart: AtomCommand<{
201
201
  readonly environmentId: EnvironmentId;
@@ -1,4 +1,4 @@
1
- import { Fs as WS_METHODS, Ur as subscribe, dr as createEnvironmentRpcCommand, hr as createEnvironmentSubscriptionAtomFamily, iD as scan, lr as createAtomCommandScheduler, mr as createEnvironmentRpcSubscriptionAtomFamily } from "../../shared.js";
1
+ import { Us as WS_METHODS, WO as scan, Yr as subscribe, hr as createEnvironmentRpcCommand, pr as createAtomCommandScheduler, vr as createEnvironmentRpcSubscriptionAtomFamily, yr as createEnvironmentSubscriptionAtomFamily } from "../../shared.js";
2
2
  //#region upstream-t3code/packages/client-runtime/src/state/terminalSession.ts
3
3
  function selectRunningSubprocessTerminalIds(sessions) {
4
4
  return sessions.filter((session) => session.state.hasRunningSubprocess).map((session) => session.target.terminalId);
@@ -0,0 +1,23 @@
1
+ import { CD as EnvironmentId, Ty as OrchestrationSearchThreadsResult, zy as OrchestrationThreadSearchMatch } from "../../shared.js";
2
+ import { AsyncResult, Atom } from "effect/unstable/reactivity";
3
+ //#region upstream-t3code/packages/client-runtime/src/state/threadSearch.d.ts
4
+ interface EnvironmentThreadSearchMatch extends OrchestrationThreadSearchMatch {
5
+ readonly environmentId: EnvironmentId;
6
+ }
7
+ interface ThreadSearchResultsState {
8
+ readonly matches: ReadonlyArray<EnvironmentThreadSearchMatch>;
9
+ readonly isLoading: boolean;
10
+ }
11
+ declare function makeThreadSearchKey(environmentIds: ReadonlyArray<EnvironmentId>, query: string): string;
12
+ declare function threadSearchMatchKey(match: Pick<EnvironmentThreadSearchMatch, "environmentId" | "threadId">): string;
13
+ /**
14
+ * Combines one search query atom per environment. Failed and disconnected
15
+ * environments contribute no content matches, preserving local title search
16
+ * as the compatibility fallback.
17
+ */
18
+ declare function createThreadSearchResultsAtomFamily<E>(options: {
19
+ readonly getSearchAtom: (environmentId: EnvironmentId, query: string) => Atom.Atom<AsyncResult.AsyncResult<OrchestrationSearchThreadsResult, E>>;
20
+ readonly labelPrefix: string;
21
+ }): (arg: string) => Atom.Atom<ThreadSearchResultsState>;
22
+ //#endregion
23
+ export { EnvironmentThreadSearchMatch, ThreadSearchResultsState, createThreadSearchResultsAtomFamily, makeThreadSearchKey, threadSearchMatchKey };
@@ -0,0 +1,38 @@
1
+ import { $r as make, Cj as getOrNull, Cw as EnvironmentId, LD as decodeUnknownSync, Zr as family, bD as ArraySchema, jD as Tuple, ni as withLabel, si as value, yx as OrchestrationSearchThreadsInput } from "../../shared.js";
2
+ const decodeThreadSearchKey = decodeUnknownSync(Tuple([ArraySchema(EnvironmentId), OrchestrationSearchThreadsInput.fields.query]));
3
+ function makeThreadSearchKey(environmentIds, query) {
4
+ return JSON.stringify([[...environmentIds].sort((left, right) => left.localeCompare(right)), query]);
5
+ }
6
+ function parseThreadSearchKey(key) {
7
+ return decodeThreadSearchKey(JSON.parse(key));
8
+ }
9
+ function threadSearchMatchKey(match) {
10
+ return JSON.stringify([match.environmentId, match.threadId]);
11
+ }
12
+ /**
13
+ * Combines one search query atom per environment. Failed and disconnected
14
+ * environments contribute no content matches, preserving local title search
15
+ * as the compatibility fallback.
16
+ */
17
+ function createThreadSearchResultsAtomFamily(options) {
18
+ return family((key) => make((get) => {
19
+ const [environmentIds, query] = parseThreadSearchKey(key);
20
+ const matches = [];
21
+ let isLoading = false;
22
+ for (const environmentId of environmentIds) {
23
+ const result = get(options.getSearchAtom(environmentId, query));
24
+ isLoading ||= result.waiting;
25
+ const value$1 = getOrNull(value(result));
26
+ if (value$1 !== null) matches.push(...value$1.matches.map((match) => ({
27
+ ...match,
28
+ environmentId
29
+ })));
30
+ }
31
+ return {
32
+ matches,
33
+ isLoading
34
+ };
35
+ }).pipe(withLabel(`${options.labelPrefix}:${key}`)));
36
+ }
37
+ //#endregion
38
+ export { createThreadSearchResultsAtomFamily, makeThreadSearchKey, threadSearchMatchKey };