@esso0428/pi-subagents 0.15.3 → 0.15.4
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/CHANGELOG.md +5 -0
- package/dist/agent-history.d.ts +2 -0
- package/dist/agent-history.js +8 -0
- package/dist/index.js +2 -2
- package/dist/ui/agent-widget.d.ts +5 -1
- package/dist/ui/agent-widget.js +12 -2
- package/dist/ui/fleet-list.d.ts +5 -3
- package/dist/ui/fleet-list.js +6 -8
- package/package.json +1 -1
- package/src/agent-history.ts +8 -1
- package/src/index.ts +5 -3
- package/src/ui/agent-widget.ts +8 -1
- package/src/ui/fleet-list.ts +7 -9
- package/test/agent-history.test.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.15.4] - 2026-09-11
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Unavailable history records no longer load the subagent panels.** Reload-restored metadata is shown in AgentWidget, FleetView, and `/agents` only when its project-local transcript still exists; stale records remain available through the normal session result/history data without creating a dead-end Enter target.
|
|
14
|
+
|
|
10
15
|
## [0.15.3] - 2026-09-11
|
|
11
16
|
|
|
12
17
|
### Added
|
package/dist/agent-history.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export declare function createAgentHistoryPath(cwd: string, agentId: string, age
|
|
|
14
14
|
export declare function agentHistoryLocator(cwd: string, historyPath: string): string;
|
|
15
15
|
/** Resolve only paths in this package's project-local transcript namespace. */
|
|
16
16
|
export declare function resolveAgentHistoryPath(cwd: string, locator: string): string | undefined;
|
|
17
|
+
/** Return true only when a valid project-local transcript file exists. */
|
|
18
|
+
export declare function hasAgentHistory(cwd: string, locator: string | undefined): boolean;
|
|
17
19
|
/**
|
|
18
20
|
* Read persisted transcript entries into the message shape used by the live
|
|
19
21
|
* conversation viewer. Malformed lines and unknown records are skipped so one
|
package/dist/agent-history.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.ensureSubagentsGitignore = ensureSubagentsGitignore;
|
|
|
5
5
|
exports.createAgentHistoryPath = createAgentHistoryPath;
|
|
6
6
|
exports.agentHistoryLocator = agentHistoryLocator;
|
|
7
7
|
exports.resolveAgentHistoryPath = resolveAgentHistoryPath;
|
|
8
|
+
exports.hasAgentHistory = hasAgentHistory;
|
|
8
9
|
exports.readAgentHistory = readAgentHistory;
|
|
9
10
|
const node_fs_1 = require("node:fs");
|
|
10
11
|
const node_path_1 = require("node:path");
|
|
@@ -58,6 +59,13 @@ function resolveAgentHistoryPath(cwd, locator) {
|
|
|
58
59
|
return undefined;
|
|
59
60
|
return candidate;
|
|
60
61
|
}
|
|
62
|
+
/** Return true only when a valid project-local transcript file exists. */
|
|
63
|
+
function hasAgentHistory(cwd, locator) {
|
|
64
|
+
if (!locator)
|
|
65
|
+
return false;
|
|
66
|
+
const path = resolveAgentHistoryPath(cwd, locator);
|
|
67
|
+
return path !== undefined && (0, node_fs_1.existsSync)(path);
|
|
68
|
+
}
|
|
61
69
|
/**
|
|
62
70
|
* Read persisted transcript entries into the message shape used by the live
|
|
63
71
|
* conversation viewer. Malformed lines and unknown records are skipped so one
|
package/dist/index.js
CHANGED
|
@@ -581,7 +581,7 @@ function default_1(pi) {
|
|
|
581
581
|
// everything else; "off" = hide the widget entirely. Read live at render time.
|
|
582
582
|
let widgetMode = "background";
|
|
583
583
|
function getWidgetMode() { return widgetMode; }
|
|
584
|
-
const widget = new agent_widget_js_1.AgentWidget(manager, agentActivity, getWidgetMode);
|
|
584
|
+
const widget = new agent_widget_js_1.AgentWidget(manager, agentActivity, getWidgetMode, () => currentCtx?.cwd);
|
|
585
585
|
function setWidgetMode(m) { widgetMode = m; widget.update(); }
|
|
586
586
|
// Claude Code-style FleetView: navigable list of main + subagents below the editor.
|
|
587
587
|
const fleet = new fleet_list_js_1.FleetList(manager, agentActivity, () => currentCtx?.cwd);
|
|
@@ -1617,7 +1617,7 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1617
1617
|
}
|
|
1618
1618
|
}
|
|
1619
1619
|
async function showRunningAgents(ctx) {
|
|
1620
|
-
const agents = manager.listAgents();
|
|
1620
|
+
const agents = manager.listAgents().filter((record) => record.session !== undefined || (0, agent_history_js_1.hasAgentHistory)(ctx.cwd, record.transcriptPath));
|
|
1621
1621
|
if (agents.length === 0) {
|
|
1622
1622
|
ctx.ui.notify("No agents.", "info");
|
|
1623
1623
|
return;
|
|
@@ -102,6 +102,8 @@ export declare class AgentWidget {
|
|
|
102
102
|
* extension supplies one defaulting to `"background"`.
|
|
103
103
|
*/
|
|
104
104
|
private mode;
|
|
105
|
+
/** Current project cwd used to validate reload-restored transcripts. */
|
|
106
|
+
private getCwd;
|
|
105
107
|
private uiCtx;
|
|
106
108
|
private widgetFrame;
|
|
107
109
|
private widgetInterval;
|
|
@@ -121,7 +123,9 @@ export declare class AgentWidget {
|
|
|
121
123
|
* `WidgetMode`. Defaults to `"all"` when a caller supplies no policy; the
|
|
122
124
|
* extension supplies one defaulting to `"background"`.
|
|
123
125
|
*/
|
|
124
|
-
mode?: () => WidgetMode
|
|
126
|
+
mode?: () => WidgetMode,
|
|
127
|
+
/** Current project cwd used to validate reload-restored transcripts. */
|
|
128
|
+
getCwd?: () => string | undefined);
|
|
125
129
|
/**
|
|
126
130
|
* Agents eligible for the widget, per the current `WidgetMode`:
|
|
127
131
|
* - `off`: none (the widget's existing empty-state path hides it entirely).
|
package/dist/ui/agent-widget.js
CHANGED
|
@@ -18,6 +18,7 @@ exports.getPromptModeLabel = getPromptModeLabel;
|
|
|
18
18
|
exports.buildInvocationTags = buildInvocationTags;
|
|
19
19
|
exports.describeActivity = describeActivity;
|
|
20
20
|
const pi_tui_1 = require("@earendil-works/pi-tui");
|
|
21
|
+
const agent_history_js_1 = require("../agent-history.js");
|
|
21
22
|
const agent_types_js_1 = require("../agent-types.js");
|
|
22
23
|
const usage_js_1 = require("../usage.js");
|
|
23
24
|
// ---- Constants ----
|
|
@@ -155,6 +156,7 @@ class AgentWidget {
|
|
|
155
156
|
manager;
|
|
156
157
|
agentActivity;
|
|
157
158
|
mode;
|
|
159
|
+
getCwd;
|
|
158
160
|
uiCtx;
|
|
159
161
|
widgetFrame = 0;
|
|
160
162
|
widgetInterval;
|
|
@@ -174,10 +176,13 @@ class AgentWidget {
|
|
|
174
176
|
* `WidgetMode`. Defaults to `"all"` when a caller supplies no policy; the
|
|
175
177
|
* extension supplies one defaulting to `"background"`.
|
|
176
178
|
*/
|
|
177
|
-
mode = () => "all"
|
|
179
|
+
mode = () => "all",
|
|
180
|
+
/** Current project cwd used to validate reload-restored transcripts. */
|
|
181
|
+
getCwd = () => undefined) {
|
|
178
182
|
this.manager = manager;
|
|
179
183
|
this.agentActivity = agentActivity;
|
|
180
184
|
this.mode = mode;
|
|
185
|
+
this.getCwd = getCwd;
|
|
181
186
|
}
|
|
182
187
|
/**
|
|
183
188
|
* Agents eligible for the widget, per the current `WidgetMode`:
|
|
@@ -191,7 +196,12 @@ class AgentWidget {
|
|
|
191
196
|
* - `all`: every agent.
|
|
192
197
|
*/
|
|
193
198
|
widgetAgents() {
|
|
194
|
-
const all = this.manager.listAgents()
|
|
199
|
+
const all = this.manager.listAgents().filter((record) => {
|
|
200
|
+
if (record.status === "running" || record.status === "queued")
|
|
201
|
+
return true;
|
|
202
|
+
const cwd = this.getCwd();
|
|
203
|
+
return cwd !== undefined && (0, agent_history_js_1.hasAgentHistory)(cwd, record.transcriptPath);
|
|
204
|
+
});
|
|
195
205
|
switch (this.mode()) {
|
|
196
206
|
case "off": return [];
|
|
197
207
|
case "background": return all.filter(a => a.isBackground !== false);
|
package/dist/ui/fleet-list.d.ts
CHANGED
|
@@ -74,9 +74,11 @@ export declare class FleetList {
|
|
|
74
74
|
update(): void;
|
|
75
75
|
/**
|
|
76
76
|
* Agents shown in the list, ordered earliest-launched first so the ones you
|
|
77
|
-
* started sooner sit at the top.
|
|
78
|
-
*
|
|
79
|
-
*
|
|
77
|
+
* started sooner sit at the top. Live rows are openable through their session;
|
|
78
|
+
* terminal rows are included only when their durable transcript exists. This
|
|
79
|
+
* prevents Enter from dead-ending on stale rehydrated metadata.
|
|
80
|
+
* Included: running/queued, plus the agent currently being viewed, plus
|
|
81
|
+
* recently-finished ones (they linger briefly before dropping out).
|
|
80
82
|
* Pending agents with no session yet are hidden until they start.
|
|
81
83
|
* (`listAgents()` is newest-first, so we re-sort.)
|
|
82
84
|
*/
|
package/dist/ui/fleet-list.js
CHANGED
|
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
exports.FleetList = void 0;
|
|
16
16
|
exports.formatFleetElapsed = formatFleetElapsed;
|
|
17
17
|
exports.formatFleetTokens = formatFleetTokens;
|
|
18
|
-
const node_fs_1 = require("node:fs");
|
|
19
18
|
const pi_tui_1 = require("@earendil-works/pi-tui");
|
|
20
19
|
const agent_history_js_1 = require("../agent-history.js");
|
|
21
20
|
const usage_js_1 = require("../usage.js");
|
|
@@ -167,9 +166,11 @@ class FleetList {
|
|
|
167
166
|
// ---- Roster ----
|
|
168
167
|
/**
|
|
169
168
|
* Agents shown in the list, ordered earliest-launched first so the ones you
|
|
170
|
-
* started sooner sit at the top.
|
|
171
|
-
*
|
|
172
|
-
*
|
|
169
|
+
* started sooner sit at the top. Live rows are openable through their session;
|
|
170
|
+
* terminal rows are included only when their durable transcript exists. This
|
|
171
|
+
* prevents Enter from dead-ending on stale rehydrated metadata.
|
|
172
|
+
* Included: running/queued, plus the agent currently being viewed, plus
|
|
173
|
+
* recently-finished ones (they linger briefly before dropping out).
|
|
173
174
|
* Pending agents with no session yet are hidden until they start.
|
|
174
175
|
* (`listAgents()` is newest-first, so we re-sort.)
|
|
175
176
|
*/
|
|
@@ -181,10 +182,7 @@ class FleetList {
|
|
|
181
182
|
|| a.id === this.viewingAgentId
|
|
182
183
|
|| (a.completedAt != null && now - a.completedAt < FINISHED_LINGER_MS));
|
|
183
184
|
const cwd = this.getCwd();
|
|
184
|
-
const
|
|
185
|
-
? (0, agent_history_js_1.resolveAgentHistoryPath)(cwd, a.transcriptPath)
|
|
186
|
-
: undefined;
|
|
187
|
-
const history = !!historyPath && (0, node_fs_1.existsSync)(historyPath);
|
|
185
|
+
const history = cwd !== undefined && (0, agent_history_js_1.hasAgentHistory)(cwd, a.transcriptPath);
|
|
188
186
|
return !!live || history;
|
|
189
187
|
})
|
|
190
188
|
.sort((a, b) => a.startedAt - b.startedAt);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@esso0428/pi-subagents",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.4",
|
|
4
4
|
"description": "A pi extension that brings smart Claude Code-style autonomous sub-agents to pi, with npm:pi-subagents-style JSON agent overrides.",
|
|
5
5
|
"author": "ESSO0428",
|
|
6
6
|
"repository": {
|
package/src/agent-history.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Durable, project-local transcript storage for subagents. */
|
|
2
2
|
|
|
3
|
-
import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
4
4
|
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
|
|
@@ -62,6 +62,13 @@ export function resolveAgentHistoryPath(cwd: string, locator: string): string |
|
|
|
62
62
|
return candidate;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/** Return true only when a valid project-local transcript file exists. */
|
|
66
|
+
export function hasAgentHistory(cwd: string, locator: string | undefined): boolean {
|
|
67
|
+
if (!locator) return false;
|
|
68
|
+
const path = resolveAgentHistoryPath(cwd, locator);
|
|
69
|
+
return path !== undefined && existsSync(path);
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
/**
|
|
66
73
|
* Read persisted transcript entries into the message shape used by the live
|
|
67
74
|
* conversation viewer. Malformed lines and unknown records are skipped so one
|
package/src/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { isModelInScope, readEnabledModels, resolveEnabledModels } from "./enabl
|
|
|
24
24
|
import { GroupJoinManager } from "./group-join.js";
|
|
25
25
|
import { resolveAgentInvocationConfig, resolveJoinMode } from "./invocation-config.js";
|
|
26
26
|
import { type ModelRegistry, resolveModel } from "./model-resolver.js";
|
|
27
|
-
import { agentHistoryLocator, createAgentHistoryPath,
|
|
27
|
+
import { agentHistoryLocator, createAgentHistoryPath, ensureSubagentsGitignore, hasAgentHistory, readAgentHistory } from "./agent-history.js";
|
|
28
28
|
import { createOutputFilePath, streamToOutputFile, writeInitialEntry } from "./output-file.js";
|
|
29
29
|
import { SubagentScheduler } from "./schedule.js";
|
|
30
30
|
import { resolveStorePath, ScheduleStore } from "./schedule-store.js";
|
|
@@ -640,7 +640,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
640
640
|
// everything else; "off" = hide the widget entirely. Read live at render time.
|
|
641
641
|
let widgetMode: WidgetMode = "background";
|
|
642
642
|
function getWidgetMode(): WidgetMode { return widgetMode; }
|
|
643
|
-
const widget = new AgentWidget(manager, agentActivity, getWidgetMode);
|
|
643
|
+
const widget = new AgentWidget(manager, agentActivity, getWidgetMode, () => currentCtx?.cwd);
|
|
644
644
|
function setWidgetMode(m: WidgetMode): void { widgetMode = m; widget.update(); }
|
|
645
645
|
|
|
646
646
|
// Claude Code-style FleetView: navigable list of main + subagents below the editor.
|
|
@@ -1789,7 +1789,9 @@ Terse command-style prompts produce shallow, generic work.
|
|
|
1789
1789
|
}
|
|
1790
1790
|
|
|
1791
1791
|
async function showRunningAgents(ctx: ExtensionCommandContext) {
|
|
1792
|
-
const agents = manager.listAgents()
|
|
1792
|
+
const agents = manager.listAgents().filter((record) =>
|
|
1793
|
+
record.session !== undefined || hasAgentHistory(ctx.cwd, record.transcriptPath),
|
|
1794
|
+
);
|
|
1793
1795
|
if (agents.length === 0) {
|
|
1794
1796
|
ctx.ui.notify("No agents.", "info");
|
|
1795
1797
|
return;
|
package/src/ui/agent-widget.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
9
|
+
import { hasAgentHistory } from "../agent-history.js";
|
|
9
10
|
import type { AgentManager } from "../agent-manager.js";
|
|
10
11
|
import { getConfig } from "../agent-types.js";
|
|
11
12
|
import type { AgentInvocation, SubagentType, WidgetMode } from "../types.js";
|
|
@@ -237,6 +238,8 @@ export class AgentWidget {
|
|
|
237
238
|
* extension supplies one defaulting to `"background"`.
|
|
238
239
|
*/
|
|
239
240
|
private mode: () => WidgetMode = () => "all",
|
|
241
|
+
/** Current project cwd used to validate reload-restored transcripts. */
|
|
242
|
+
private getCwd: () => string | undefined = () => undefined,
|
|
240
243
|
) {}
|
|
241
244
|
|
|
242
245
|
/**
|
|
@@ -251,7 +254,11 @@ export class AgentWidget {
|
|
|
251
254
|
* - `all`: every agent.
|
|
252
255
|
*/
|
|
253
256
|
private widgetAgents() {
|
|
254
|
-
const all = this.manager.listAgents()
|
|
257
|
+
const all = this.manager.listAgents().filter((record) => {
|
|
258
|
+
if (record.status === "running" || record.status === "queued") return true;
|
|
259
|
+
const cwd = this.getCwd();
|
|
260
|
+
return cwd !== undefined && hasAgentHistory(cwd, record.transcriptPath);
|
|
261
|
+
});
|
|
255
262
|
switch (this.mode()) {
|
|
256
263
|
case "off": return [];
|
|
257
264
|
case "background": return all.filter(a => a.isBackground !== false);
|
package/src/ui/fleet-list.ts
CHANGED
|
@@ -11,10 +11,9 @@
|
|
|
11
11
|
* can `consume` keys — gated on `getEditorText() === ""` so normal typing is untouched.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { existsSync } from "node:fs";
|
|
15
14
|
import { Editor, isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
16
15
|
import type { AgentManager } from "../agent-manager.js";
|
|
17
|
-
import {
|
|
16
|
+
import { hasAgentHistory, readAgentHistory } from "../agent-history.js";
|
|
18
17
|
import type { AgentRecord } from "../types.js";
|
|
19
18
|
import { getLifetimeTotal } from "../usage.js";
|
|
20
19
|
import { type AgentActivity, getDisplayName, type Theme } from "./agent-widget.js";
|
|
@@ -182,9 +181,11 @@ export class FleetList {
|
|
|
182
181
|
|
|
183
182
|
/**
|
|
184
183
|
* Agents shown in the list, ordered earliest-launched first so the ones you
|
|
185
|
-
* started sooner sit at the top.
|
|
186
|
-
*
|
|
187
|
-
*
|
|
184
|
+
* started sooner sit at the top. Live rows are openable through their session;
|
|
185
|
+
* terminal rows are included only when their durable transcript exists. This
|
|
186
|
+
* prevents Enter from dead-ending on stale rehydrated metadata.
|
|
187
|
+
* Included: running/queued, plus the agent currently being viewed, plus
|
|
188
|
+
* recently-finished ones (they linger briefly before dropping out).
|
|
188
189
|
* Pending agents with no session yet are hidden until they start.
|
|
189
190
|
* (`listAgents()` is newest-first, so we re-sort.)
|
|
190
191
|
*/
|
|
@@ -198,10 +199,7 @@ export class FleetList {
|
|
|
198
199
|
|| (a.completedAt != null && now - a.completedAt < FINISHED_LINGER_MS)
|
|
199
200
|
);
|
|
200
201
|
const cwd = this.getCwd();
|
|
201
|
-
const
|
|
202
|
-
? resolveAgentHistoryPath(cwd, a.transcriptPath)
|
|
203
|
-
: undefined;
|
|
204
|
-
const history = !!historyPath && existsSync(historyPath);
|
|
202
|
+
const history = cwd !== undefined && hasAgentHistory(cwd, a.transcriptPath);
|
|
205
203
|
return !!live || history;
|
|
206
204
|
})
|
|
207
205
|
.sort((a, b) => a.startedAt - b.startedAt);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
agentHistoryLocator,
|
|
7
7
|
createAgentHistoryPath,
|
|
8
8
|
ensureSubagentsGitignore,
|
|
9
|
+
hasAgentHistory,
|
|
9
10
|
readAgentHistory,
|
|
10
11
|
resolveAgentHistoryPath,
|
|
11
12
|
} from "../src/agent-history.js";
|
|
@@ -48,10 +49,13 @@ describe("project-local subagent history", () => {
|
|
|
48
49
|
|
|
49
50
|
expect(locator).toBe(".pi-subagents/agent-transcripts/agent-1_Explore_transcript.jsonl");
|
|
50
51
|
expect(resolveAgentHistoryPath(cwd, locator)).toBe(historyPath);
|
|
52
|
+
expect(hasAgentHistory(cwd, locator)).toBe(false);
|
|
53
|
+
expect(hasAgentHistory(cwd, ".pi-subagents/other.jsonl")).toBe(false);
|
|
51
54
|
expect(resolveAgentHistoryPath(cwd, ".pi-subagents/other.jsonl")).toBeUndefined();
|
|
52
55
|
expect(resolveAgentHistoryPath(cwd, ".pi-subagents/agent-transcripts/../other.jsonl")).toBeUndefined();
|
|
53
56
|
|
|
54
57
|
writeInitialEntry(historyPath, "agent-1", "hello", cwd);
|
|
58
|
+
expect(hasAgentHistory(cwd, locator)).toBe(true);
|
|
55
59
|
const messages = readAgentHistory(cwd, locator);
|
|
56
60
|
expect(messages).toHaveLength(1);
|
|
57
61
|
expect(messages?.[0]).toMatchObject({ role: "user", content: "hello" });
|