pi-fabric 0.83.0 → 0.84.0
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.
- package/README.md +1 -1
- package/dist/chunks/{chunk-PGN56E56.js → chunk-HOKY6UCZ.js} +8 -3
- package/dist/chunks/chunk-HOKY6UCZ.js.map +7 -0
- package/dist/index.js +137 -28
- package/dist/index.js.map +3 -3
- package/dist/ui/controller.d.ts.map +1 -1
- package/dist/ui/conversation-chrome.d.ts +1 -0
- package/dist/ui/conversation-chrome.d.ts.map +1 -1
- package/dist/ui/conversation-chrome.js +1 -1
- package/dist/ui/conversation-commands.d.ts +12 -0
- package/dist/ui/conversation-commands.d.ts.map +1 -0
- package/dist/ui/conversation-history.d.ts +6 -0
- package/dist/ui/conversation-history.d.ts.map +1 -0
- package/dist/ui/conversation-native-reader-checkpoint.d.ts +8 -0
- package/dist/ui/conversation-native-reader-checkpoint.d.ts.map +1 -0
- package/dist/ui/conversation-native-reader-replay.d.ts +10 -0
- package/dist/ui/conversation-native-reader-replay.d.ts.map +1 -0
- package/dist/ui/conversation-native-reader.d.ts +5 -1
- package/dist/ui/conversation-native-reader.d.ts.map +1 -1
- package/dist/ui/conversation-native-reader.js +399 -67
- package/dist/ui/conversation-native-reader.js.map +4 -4
- package/dist/ui/conversation-queue-store.d.ts +2 -0
- package/dist/ui/conversation-queue-store.d.ts.map +1 -1
- package/dist/ui/conversation-render.d.ts +22 -10
- package/dist/ui/conversation-render.d.ts.map +1 -1
- package/dist/ui/conversation-selection.d.ts +18 -0
- package/dist/ui/conversation-selection.d.ts.map +1 -0
- package/dist/ui/conversation-shortcut.d.ts +1 -1
- package/dist/ui/conversation.d.ts +47 -1
- package/dist/ui/conversation.d.ts.map +1 -1
- package/dist/ui/conversation.js +982 -411
- package/dist/ui/conversation.js.map +4 -4
- package/dist/ui/snapshot.d.ts +9 -1
- package/dist/ui/snapshot.d.ts.map +1 -1
- package/docs/interface.md +31 -3
- package/package.json +3 -2
- package/skills/fabric-exec/references/agents.md +1 -1
- package/dist/chunks/chunk-PGN56E56.js.map +0 -7
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ The foundation is the `fabric-exec` reference skill: the model loads it before i
|
|
|
114
114
|
|
|
115
115
|
## Agent conversations
|
|
116
116
|
|
|
117
|
-
Press **
|
|
117
|
+
Press **ctrl+shift+a** or run **`/fabric chat <agent-id-or-name>`** to open a live, full-screen child conversation with a multiline editor. Send steering or follow-ups directly, switch between nested agents, and return to Main without stopping its work. Drafts and scroll positions stay with each conversation. Completed one-shot agents are read-only; persistent actors accept further messages. Drag to select transcript text, use `/copy` or `/copy selection`, and type `/help` for the small set of preview-local commands with slash completion. See [focused conversations](docs/interface.md#focused-conversations) for controls and current limitations.
|
|
118
118
|
|
|
119
119
|
## The dashboard
|
|
120
120
|
|
|
@@ -16,7 +16,8 @@ function readConversationAppearance(cwd, agentDir, projectTrusted) {
|
|
|
16
16
|
codeBlockIndent: settings.getCodeBlockIndent(),
|
|
17
17
|
hideThinkingBlock: settings.getHideThinkingBlock(),
|
|
18
18
|
showImages: settings.getShowImages(),
|
|
19
|
-
imageWidthCells: settings.getImageWidthCells()
|
|
19
|
+
imageWidthCells: settings.getImageWidthCells(),
|
|
20
|
+
copyOnSelect: settings.getFullscreenCopyOnSelect()
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
var footerPath = (cwd) => {
|
|
@@ -29,6 +30,10 @@ function conversationFooter(target, theme, width) {
|
|
|
29
30
|
const location = target.cwd ? footerPath(target.cwd) : "cwd unavailable";
|
|
30
31
|
const branch = target.branch ? ` (${target.branch})` : "";
|
|
31
32
|
const pwd = safeText(`${location}${branch} \u2022 ${target.name}`);
|
|
33
|
+
const access = target.readOnlyReason ? theme.fg("warning", "read-only") : "";
|
|
34
|
+
const separator = theme.fg("dim", " \u2022 ");
|
|
35
|
+
const locationWidth = width - (access ? visibleWidth(separator) + visibleWidth(access) : 0);
|
|
36
|
+
const pathLine = locationWidth > 0 ? theme.fg("dim", truncateToWidth(pwd, locationWidth, "\u2026")) + (access ? separator + access : "") : truncateToWidth(access, width, "\u2026");
|
|
32
37
|
const stats = [safeText(target.status)];
|
|
33
38
|
if (target.usage) {
|
|
34
39
|
const usage = target.usage;
|
|
@@ -46,7 +51,7 @@ function conversationFooter(target, theme, width) {
|
|
|
46
51
|
const right = truncateToWidth(`${model}${thinking}`, Math.max(0, width - visibleWidth(left) - 2), "");
|
|
47
52
|
const gap = " ".repeat(Math.max(0, width - visibleWidth(left) - visibleWidth(right)));
|
|
48
53
|
return [
|
|
49
|
-
|
|
54
|
+
pathLine,
|
|
50
55
|
theme.fg("dim", `${left}${gap}${right}`)
|
|
51
56
|
];
|
|
52
57
|
}
|
|
@@ -55,4 +60,4 @@ export {
|
|
|
55
60
|
readConversationAppearance,
|
|
56
61
|
conversationFooter
|
|
57
62
|
};
|
|
58
|
-
//# sourceMappingURL=chunk-
|
|
63
|
+
//# sourceMappingURL=chunk-HOKY6UCZ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/conversation-chrome.ts"],
|
|
4
|
+
"sourcesContent": ["import { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { SettingsManager, type Theme } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport { formatTokens, safeText } from \"./format.js\";\nimport type { FabricConversationTarget } from \"./conversation.js\";\n\nexport interface FabricConversationAppearance {\n editorPaddingX: number;\n outputPad: 0 | 1;\n codeBlockIndent?: string;\n hideThinkingBlock?: boolean;\n showImages?: boolean;\n imageWidthCells?: number;\n copyOnSelect?: boolean;\n}\n\n/** Read the host's public Pi settings without changing its editor or footer. */\nexport function readConversationAppearance(cwd: string, agentDir: string, projectTrusted: boolean): FabricConversationAppearance {\n const settings = SettingsManager.create(cwd, agentDir, { projectTrusted });\n return {\n editorPaddingX: settings.getEditorPaddingX(), outputPad: settings.getOutputPad(),\n codeBlockIndent: settings.getCodeBlockIndent(), hideThinkingBlock: settings.getHideThinkingBlock(),\n showImages: settings.getShowImages(), imageWidthCells: settings.getImageWidthCells(),\n copyOnSelect: settings.getFullscreenCopyOnSelect(),\n };\n}\n\nconst footerPath = (cwd: string): string => {\n const home = homedir();\n if (cwd === home) return \"~\";\n return cwd.startsWith(`${home}${path.sep}`) ? `~${cwd.slice(home.length)}` : cwd;\n};\n\n/** Native-shaped footer backed by this participant, never by Main's session stats. */\nexport function conversationFooter(target: FabricConversationTarget | undefined, theme: Theme, width: number): string[] {\n if (!target || width <= 0) return [];\n const location = target.cwd ? footerPath(target.cwd) : \"cwd unavailable\";\n const branch = target.branch ? ` (${target.branch})` : \"\";\n const pwd = safeText(`${location}${branch} \u2022 ${target.name}`);\n const access = target.readOnlyReason ? theme.fg(\"warning\", \"read-only\") : \"\";\n const separator = theme.fg(\"dim\", \" \u2022 \");\n const locationWidth = width - (access ? visibleWidth(separator) + visibleWidth(access) : 0);\n const pathLine = locationWidth > 0\n ? theme.fg(\"dim\", truncateToWidth(pwd, locationWidth, \"\u2026\")) + (access ? separator + access : \"\")\n : truncateToWidth(access, width, \"\u2026\");\n const stats = [safeText(target.status)];\n if (target.usage) {\n const usage = target.usage;\n // Agent usage is run-scoped; actor values describe the current/last activation.\n stats.push(\"run\", `\u2191${formatTokens(usage.input)}`, `\u2193${formatTokens(usage.output)}`);\n if (usage.cacheRead > 0) stats.push(`R${formatTokens(usage.cacheRead)}`);\n if (usage.cacheWrite > 0) stats.push(`W${formatTokens(usage.cacheWrite)}`);\n if (Number.isFinite(usage.cost)) stats.push(`$${usage.cost.toFixed(3)}`);\n } else {\n stats.push(\"usage unavailable\");\n }\n // Cumulative run tokens are not current context occupancy. Do not invent a percentage.\n stats.push(target.contextWindow ? `?/${formatTokens(target.contextWindow)} ctx` : \"ctx ?\");\n const model = safeText(target.model ?? `${target.runner ?? \"\"} model unavailable`).trim();\n const thinking = target.thinking ? ` \u2022 ${safeText(target.thinking === \"off\" ? \"thinking off\" : target.thinking)}` : \"\";\n const left = truncateToWidth(stats.join(\" \"), width, \"\u2026\");\n const right = truncateToWidth(`${model}${thinking}`, Math.max(0, width - visibleWidth(left) - 2), \"\");\n const gap = \" \".repeat(Math.max(0, width - visibleWidth(left) - visibleWidth(right)));\n return [\n pathLine,\n theme.fg(\"dim\", `${left}${gap}${right}`),\n ];\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -721,7 +721,7 @@ var autoArmFabricPrewalk = async (state, context, pi) => {
|
|
|
721
721
|
|
|
722
722
|
// src/ui/conversation-shortcut.ts
|
|
723
723
|
var FABRIC_CONVERSATION_SHORTCUT = "ctrl+shift+a";
|
|
724
|
-
var FABRIC_CONVERSATION_HINT = "
|
|
724
|
+
var FABRIC_CONVERSATION_HINT = "ctrl+shift+a chat";
|
|
725
725
|
|
|
726
726
|
// src/commands/fabric.ts
|
|
727
727
|
import fs from "node:fs";
|
|
@@ -3930,6 +3930,7 @@ var piHostCompatibilityWarning = (version = detectPiHostVersion()) => {
|
|
|
3930
3930
|
import path8 from "node:path";
|
|
3931
3931
|
|
|
3932
3932
|
// src/ui/snapshot.ts
|
|
3933
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3933
3934
|
var MAX_UI_AGENTS = 240;
|
|
3934
3935
|
var boundedUiAgents = (local, remote) => {
|
|
3935
3936
|
const selected = /* @__PURE__ */ new Map();
|
|
@@ -3969,9 +3970,46 @@ var stateEntry = (entry) => {
|
|
|
3969
3970
|
...detail ? { detail: detail.slice(0, 500) } : {}
|
|
3970
3971
|
};
|
|
3971
3972
|
};
|
|
3972
|
-
var
|
|
3973
|
+
var FabricDashboardSnapshotCache = class {
|
|
3974
|
+
inputs;
|
|
3975
|
+
snapshot;
|
|
3976
|
+
get(inputs) {
|
|
3977
|
+
return this.snapshot && isDeepStrictEqual(this.inputs, inputs) ? { ...this.snapshot, now: Date.now() } : void 0;
|
|
3978
|
+
}
|
|
3979
|
+
set(inputs, snapshot) {
|
|
3980
|
+
this.inputs = structuredClone(inputs);
|
|
3981
|
+
this.snapshot = snapshot;
|
|
3982
|
+
}
|
|
3983
|
+
clear() {
|
|
3984
|
+
this.inputs = void 0;
|
|
3985
|
+
this.snapshot = void 0;
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
var createDashboardSnapshot = (state, events, context, activityRuns, cache) => {
|
|
3973
3989
|
const runs = activityRuns ?? state.activity.runs();
|
|
3974
3990
|
const agentRecords = typeof state.agents.listForUi === "function" ? state.agents.listForUi() : state.agents.list();
|
|
3991
|
+
const participants = typeof state.participantInfos === "function" ? state.participantInfos({ scope: "project" }) : [];
|
|
3992
|
+
const actorRecords = state.actors.list();
|
|
3993
|
+
const main = state.mainAgentInfo(context);
|
|
3994
|
+
const peers = typeof state.peerInfos === "function" ? state.peerInfos() : [];
|
|
3995
|
+
const globalActors = state.globalActors.list();
|
|
3996
|
+
const componentGraph = typeof state.componentGraph === "function" ? state.componentGraph() : { components: [], edges: [], cycles: [] };
|
|
3997
|
+
const meshEntries = state.config.mesh.enabled ? state.mesh.list("", 200) : [];
|
|
3998
|
+
const inputs = {
|
|
3999
|
+
runs,
|
|
4000
|
+
agentRecords,
|
|
4001
|
+
actorRecords,
|
|
4002
|
+
participants,
|
|
4003
|
+
main,
|
|
4004
|
+
peers,
|
|
4005
|
+
globalActors,
|
|
4006
|
+
componentGraph,
|
|
4007
|
+
meshEntries,
|
|
4008
|
+
events,
|
|
4009
|
+
widgetDismissedAt: state.widgetDismissedAt
|
|
4010
|
+
};
|
|
4011
|
+
const previous = cache?.get(inputs);
|
|
4012
|
+
if (previous) return previous;
|
|
3975
4013
|
const agentLinks = [];
|
|
3976
4014
|
for (const run of runs) {
|
|
3977
4015
|
for (const call of run.calls) {
|
|
@@ -4035,15 +4073,18 @@ var createDashboardSnapshot = (state, events, context, activityRuns) => {
|
|
|
4035
4073
|
}
|
|
4036
4074
|
};
|
|
4037
4075
|
for (const record of agentRecords) appendAgent(record, 0);
|
|
4038
|
-
const
|
|
4076
|
+
const workerByActor = /* @__PURE__ */ new Map();
|
|
4077
|
+
for (const agent of allAgents) {
|
|
4078
|
+
if (!agent.actorId) continue;
|
|
4079
|
+
const previous2 = workerByActor.get(agent.actorId);
|
|
4080
|
+
const active = Number(activeStatuses.has(agent.status)) - Number(previous2 !== void 0 && activeStatuses.has(previous2.status));
|
|
4081
|
+
const recency = (numberFrom(agent.updatedAt) ?? numberFrom(agent.startedAt) ?? 0) - (numberFrom(previous2?.updatedAt) ?? numberFrom(previous2?.startedAt) ?? 0);
|
|
4082
|
+
if (!previous2 || (active || recency) > 0) workerByActor.set(agent.actorId, agent);
|
|
4083
|
+
}
|
|
4039
4084
|
const participantById = new Map(participants.map((participant) => [participant.id, participant]));
|
|
4040
|
-
const actors =
|
|
4085
|
+
const actors = actorRecords.map((actor) => {
|
|
4041
4086
|
const participant = participantById.get(actor.id);
|
|
4042
|
-
const worker =
|
|
4043
|
-
const active = Number(activeStatuses.has(right.status)) - Number(activeStatuses.has(left.status));
|
|
4044
|
-
const recency = (numberFrom(right.updatedAt) ?? numberFrom(right.startedAt) ?? 0) - (numberFrom(left.updatedAt) ?? numberFrom(left.startedAt) ?? 0);
|
|
4045
|
-
return active || recency;
|
|
4046
|
-
})[0];
|
|
4087
|
+
const worker = workerByActor.get(actor.id);
|
|
4047
4088
|
return {
|
|
4048
4089
|
...actor,
|
|
4049
4090
|
instructions: state.actors.instructions(actor.id),
|
|
@@ -4102,7 +4143,6 @@ var createDashboardSnapshot = (state, events, context, activityRuns) => {
|
|
|
4102
4143
|
const rightActive = activeRunIds.has(right.run.id) ? 1 : 0;
|
|
4103
4144
|
return rightActive - leftActive || left.index - right.index;
|
|
4104
4145
|
}).map(({ run }) => run);
|
|
4105
|
-
const meshEntries = state.config.mesh.enabled ? state.mesh.list("", 200) : [];
|
|
4106
4146
|
const stateEntries = meshEntries.filter(
|
|
4107
4147
|
(entry) => !entry.key.startsWith("actors/") && !entry.key.startsWith("sessions/") && !entry.key.startsWith("topology/")
|
|
4108
4148
|
).map(stateEntry).sort((left, right) => {
|
|
@@ -4110,16 +4150,16 @@ var createDashboardSnapshot = (state, events, context, activityRuns) => {
|
|
|
4110
4150
|
const rightActive = activeStatuses.has(right.status) ? 1 : 0;
|
|
4111
4151
|
return rightActive - leftActive || right.updatedAt - left.updatedAt;
|
|
4112
4152
|
}).slice(0, 120);
|
|
4113
|
-
|
|
4153
|
+
const snapshot = {
|
|
4114
4154
|
now: Date.now(),
|
|
4115
4155
|
runs: orderedRuns,
|
|
4116
|
-
main
|
|
4117
|
-
peers
|
|
4156
|
+
main,
|
|
4157
|
+
peers,
|
|
4118
4158
|
participants,
|
|
4119
4159
|
widgetDismissedAt: state.widgetDismissedAt,
|
|
4120
|
-
globalActors
|
|
4160
|
+
globalActors,
|
|
4121
4161
|
agents: visibleAgents,
|
|
4122
|
-
componentGraph
|
|
4162
|
+
componentGraph,
|
|
4123
4163
|
actors: actors.sort((left, right) => {
|
|
4124
4164
|
const leftActive = activeStatuses.has(left.status) ? 1 : 0;
|
|
4125
4165
|
const rightActive = activeStatuses.has(right.status) ? 1 : 0;
|
|
@@ -4128,6 +4168,8 @@ var createDashboardSnapshot = (state, events, context, activityRuns) => {
|
|
|
4128
4168
|
state: stateEntries,
|
|
4129
4169
|
events: events.map((event) => structuredClone(event))
|
|
4130
4170
|
};
|
|
4171
|
+
cache?.set(inputs, snapshot);
|
|
4172
|
+
return snapshot;
|
|
4131
4173
|
};
|
|
4132
4174
|
|
|
4133
4175
|
// src/ui/widget.ts
|
|
@@ -4361,6 +4403,9 @@ var FabricUiController = class {
|
|
|
4361
4403
|
#activityRuns = [];
|
|
4362
4404
|
#transcripts = new AgentTranscriptReader();
|
|
4363
4405
|
#conversationReaders = /* @__PURE__ */ new Map();
|
|
4406
|
+
#activeConversationReader;
|
|
4407
|
+
#snapshotCache = new FabricDashboardSnapshotCache();
|
|
4408
|
+
#refreshGeneration = 0;
|
|
4364
4409
|
start(context) {
|
|
4365
4410
|
this.stop();
|
|
4366
4411
|
this.#context = context;
|
|
@@ -4415,6 +4460,8 @@ var FabricUiController = class {
|
|
|
4415
4460
|
this.#transcripts.clear();
|
|
4416
4461
|
for (const reader of this.#conversationReaders.values()) reader.clear();
|
|
4417
4462
|
this.#conversationReaders.clear();
|
|
4463
|
+
this.#activeConversationReader = void 0;
|
|
4464
|
+
this.#snapshotCache.clear();
|
|
4418
4465
|
}
|
|
4419
4466
|
/** True while Fabric owns keyboard input, including asynchronous view setup. */
|
|
4420
4467
|
get ownsInput() {
|
|
@@ -4450,13 +4497,64 @@ var FabricUiController = class {
|
|
|
4450
4497
|
}
|
|
4451
4498
|
if (initialTarget?.kind === "main") return;
|
|
4452
4499
|
this.#conversationState ??= new FabricConversationState();
|
|
4500
|
+
let sourceAgents;
|
|
4501
|
+
let sourceActors;
|
|
4502
|
+
let agentsById = /* @__PURE__ */ new Map();
|
|
4503
|
+
let actorsById = /* @__PURE__ */ new Map();
|
|
4504
|
+
let targetDomains = [];
|
|
4505
|
+
let baseTargets = [];
|
|
4506
|
+
let enrichedTargets = [];
|
|
4507
|
+
let targetsById = /* @__PURE__ */ new Map();
|
|
4508
|
+
let modelGeneration = -1;
|
|
4509
|
+
let modelWindows = /* @__PURE__ */ new Map();
|
|
4510
|
+
const targets = () => {
|
|
4511
|
+
const snapshot = this.#snapshot;
|
|
4512
|
+
const domains = [snapshot.main, snapshot.agents, snapshot.actors, snapshot.peers, snapshot.participants];
|
|
4513
|
+
const changed = domains.some((domain, index) => domain !== targetDomains[index]);
|
|
4514
|
+
if (changed) {
|
|
4515
|
+
targetDomains = domains;
|
|
4516
|
+
baseTargets = conversationTargets(snapshot);
|
|
4517
|
+
}
|
|
4518
|
+
if (changed || modelGeneration !== this.#refreshGeneration) {
|
|
4519
|
+
modelGeneration = this.#refreshGeneration;
|
|
4520
|
+
const windows = /* @__PURE__ */ new Map();
|
|
4521
|
+
for (const target of baseTargets) {
|
|
4522
|
+
if (!target.model || windows.has(target.model)) continue;
|
|
4523
|
+
const split = target.model.indexOf("/");
|
|
4524
|
+
if (split < 1) continue;
|
|
4525
|
+
windows.set(target.model, context.modelRegistry?.find?.(
|
|
4526
|
+
target.model.slice(0, split),
|
|
4527
|
+
target.model.slice(split + 1)
|
|
4528
|
+
)?.contextWindow);
|
|
4529
|
+
}
|
|
4530
|
+
if (changed || windows.size !== modelWindows.size || [...windows].some(([model, window]) => modelWindows.get(model) !== window)) {
|
|
4531
|
+
enrichedTargets = baseTargets.map((target) => {
|
|
4532
|
+
const contextWindow = target.model ? windows.get(target.model) : void 0;
|
|
4533
|
+
return contextWindow !== void 0 ? { ...target, contextWindow } : target;
|
|
4534
|
+
});
|
|
4535
|
+
targetsById = new Map(enrichedTargets.map((target) => [target.id, target]));
|
|
4536
|
+
}
|
|
4537
|
+
modelWindows = windows;
|
|
4538
|
+
}
|
|
4539
|
+
return enrichedTargets;
|
|
4540
|
+
};
|
|
4453
4541
|
const source = (id) => {
|
|
4454
|
-
|
|
4542
|
+
if (sourceActors !== this.#snapshot.actors) {
|
|
4543
|
+
sourceActors = this.#snapshot.actors;
|
|
4544
|
+
actorsById = new Map(sourceActors.map((actor2) => [actor2.id, actor2]));
|
|
4545
|
+
}
|
|
4546
|
+
if (sourceAgents !== this.#snapshot.agents) {
|
|
4547
|
+
sourceAgents = this.#snapshot.agents;
|
|
4548
|
+
agentsById = new Map(sourceAgents.map((agent2) => [agent2.id, agent2]));
|
|
4549
|
+
}
|
|
4550
|
+
const actor = actorsById.get(id);
|
|
4455
4551
|
if (actor) return { ...this.#actorTranscriptSource(actor), id, ...actor.sessionFile ? { sessionFile: actor.sessionFile } : {} };
|
|
4456
|
-
const agent =
|
|
4552
|
+
const agent = agentsById.get(id);
|
|
4457
4553
|
return agent ? this.#agentTranscriptSource(agent) : { id, status: "unavailable" };
|
|
4458
4554
|
};
|
|
4459
4555
|
const readerFor = (id) => {
|
|
4556
|
+
if (this.#activeConversationReader !== id) this.#suspendConversationReader();
|
|
4557
|
+
this.#activeConversationReader = id;
|
|
4460
4558
|
let reader = this.#conversationReaders.get(id);
|
|
4461
4559
|
if (!reader) {
|
|
4462
4560
|
reader = new NativeConversationReader();
|
|
@@ -4469,7 +4567,8 @@ var FabricUiController = class {
|
|
|
4469
4567
|
throw new Error("This Fabric conversation is no longer attached");
|
|
4470
4568
|
}
|
|
4471
4569
|
this.#refresh();
|
|
4472
|
-
|
|
4570
|
+
targets();
|
|
4571
|
+
const target = targetsById.get(id);
|
|
4473
4572
|
const allowed = action === "stop" ? target?.canStop : action === "steer" ? target?.canSteer : target?.canFollowUp;
|
|
4474
4573
|
if (!target || target.kind === "main" || !allowed) {
|
|
4475
4574
|
throw new Error(target?.readOnlyReason ?? `Participant ${id} cannot receive ${action}`);
|
|
@@ -4485,12 +4584,10 @@ var FabricUiController = class {
|
|
|
4485
4584
|
this.#conversationTui = tui;
|
|
4486
4585
|
this.#closeConversation = () => done(void 0);
|
|
4487
4586
|
const view = new FabricConversationView(tui, theme, {
|
|
4488
|
-
targets
|
|
4489
|
-
|
|
4490
|
-
if (
|
|
4491
|
-
|
|
4492
|
-
return model ? { ...target, contextWindow: model.contextWindow } : target;
|
|
4493
|
-
}),
|
|
4587
|
+
targets,
|
|
4588
|
+
onTargetChange: (id) => {
|
|
4589
|
+
if (this.#activeConversationReader !== id) this.#suspendConversationReader();
|
|
4590
|
+
},
|
|
4494
4591
|
appearance: readConversationAppearance(context.cwd, resolveAgentDir(), context.isProjectTrusted?.() ?? false),
|
|
4495
4592
|
...initialTarget ? { initialTargetId: initialTarget.id } : {},
|
|
4496
4593
|
state: this.#conversationState,
|
|
@@ -4543,6 +4640,7 @@ var FabricUiController = class {
|
|
|
4543
4640
|
} finally {
|
|
4544
4641
|
if (!alreadyOpen && epoch === this.#epoch) {
|
|
4545
4642
|
this.#conversationView?.dispose();
|
|
4643
|
+
this.#suspendConversationReader();
|
|
4546
4644
|
this.#conversationView = void 0;
|
|
4547
4645
|
this.#conversationTui = void 0;
|
|
4548
4646
|
this.#closeConversation = void 0;
|
|
@@ -4751,7 +4849,7 @@ var FabricUiController = class {
|
|
|
4751
4849
|
if (!this.ownsInput && !active) return;
|
|
4752
4850
|
this.#timer = setTimeout(() => {
|
|
4753
4851
|
this.#timer = void 0;
|
|
4754
|
-
this.#refresh();
|
|
4852
|
+
this.#refresh(false);
|
|
4755
4853
|
this.#schedulePoll();
|
|
4756
4854
|
}, this.state.config.ui.refreshMs);
|
|
4757
4855
|
this.#timer.unref();
|
|
@@ -4791,7 +4889,13 @@ var FabricUiController = class {
|
|
|
4791
4889
|
#transcriptSource(target) {
|
|
4792
4890
|
return "recentMessages" in target ? this.#actorTranscriptSource(target) : this.#agentTranscriptSource(target);
|
|
4793
4891
|
}
|
|
4794
|
-
#
|
|
4892
|
+
#suspendConversationReader() {
|
|
4893
|
+
if (!this.#activeConversationReader) return;
|
|
4894
|
+
const reader = this.#conversationReaders.get(this.#activeConversationReader);
|
|
4895
|
+
reader?.suspend?.();
|
|
4896
|
+
this.#activeConversationReader = void 0;
|
|
4897
|
+
}
|
|
4898
|
+
#refresh(force = true) {
|
|
4795
4899
|
this.#lastRefreshAt = performance.now();
|
|
4796
4900
|
const context = this.#context;
|
|
4797
4901
|
if (!context || !this.state.initialized) return;
|
|
@@ -4804,14 +4908,19 @@ var FabricUiController = class {
|
|
|
4804
4908
|
this.#activityRevision = revision;
|
|
4805
4909
|
this.#activityRunsDetailed = detailed;
|
|
4806
4910
|
}
|
|
4911
|
+
if (force || this.#dashboardOpen) this.#snapshotCache.clear();
|
|
4912
|
+
this.#refreshGeneration++;
|
|
4807
4913
|
this.#snapshot = createDashboardSnapshot(
|
|
4808
4914
|
this.state,
|
|
4809
4915
|
this.#events,
|
|
4810
4916
|
context,
|
|
4811
|
-
this.#activityRuns
|
|
4917
|
+
this.#activityRuns,
|
|
4918
|
+
this.#dashboardOpen ? void 0 : this.#snapshotCache
|
|
4812
4919
|
);
|
|
4813
4920
|
this.#renderWidget(context);
|
|
4814
|
-
if (this.#conversationTui
|
|
4921
|
+
if (this.#conversationTui && this.#conversationView?.refresh?.() !== false) {
|
|
4922
|
+
this.#conversationTui.requestRender();
|
|
4923
|
+
}
|
|
4815
4924
|
if (this.#dashboardTui) this.#dashboardTui.requestRender();
|
|
4816
4925
|
else if (this.#widgetTui && this.#widget?.hasChanged()) this.#widgetTui.requestRender();
|
|
4817
4926
|
} catch (error) {
|