@pi-unipi/unipi 2.6.0 → 2.6.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.
- package/CHANGELOG.md +19 -0
- package/package.json +21 -22
- package/packages/ask-user/package.json +2 -2
- package/packages/ask-user/settings-tui.ts +18 -34
- package/packages/autocomplete/package.json +1 -1
- package/packages/autocomplete/src/constants.ts +2 -15
- package/packages/autocomplete/src/provider.ts +3 -17
- package/packages/btw/extensions/btw.ts +18 -45
- package/packages/btw/package.json +2 -2
- package/packages/compactor/README.md +2 -2
- package/packages/compactor/package.json +3 -3
- package/packages/compactor/skills/compactor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-detail/SKILL.md +3 -5
- package/packages/compactor/skills/compactor-doctor/SKILL.md +1 -1
- package/packages/compactor/skills/compactor-stats/SKILL.md +1 -1
- package/packages/compactor/src/commands/index.ts +47 -78
- package/packages/compactor/src/compaction/brief.ts +161 -90
- package/packages/compactor/src/compaction/build-sections.ts +3 -4
- package/packages/compactor/src/compaction/compact-args.ts +86 -0
- package/packages/compactor/src/compaction/cut.ts +270 -28
- package/packages/compactor/src/compaction/drill-down.ts +261 -0
- package/packages/compactor/src/compaction/format-recall.ts +96 -0
- package/packages/compactor/src/compaction/format.ts +8 -3
- package/packages/compactor/src/compaction/hooks.ts +248 -72
- package/packages/compactor/src/compaction/merge.ts +34 -4
- package/packages/compactor/src/compaction/rank.ts +270 -0
- package/packages/compactor/src/compaction/recall-scope.ts +28 -0
- package/packages/compactor/src/compaction/search-entries.ts +333 -96
- package/packages/compactor/src/compaction/skill-collapse.ts +35 -0
- package/packages/compactor/src/compaction/summarize.ts +37 -6
- package/packages/compactor/src/compaction/token-estimate.ts +104 -0
- package/packages/compactor/src/compaction/touched-files.ts +35 -0
- package/packages/compactor/src/config/manager.ts +2 -27
- package/packages/compactor/src/config/presets.ts +0 -2
- package/packages/compactor/src/config/schema.ts +2 -16
- package/packages/compactor/src/executor/executor.ts +6 -15
- package/packages/compactor/src/executor/runtime.ts +2 -12
- package/packages/compactor/src/index.ts +4 -121
- package/packages/compactor/src/info-screen.ts +3 -10
- package/packages/compactor/src/security/evaluator.ts +0 -53
- package/packages/compactor/src/security/policy.ts +7 -8
- package/packages/compactor/src/session/db.ts +0 -6
- package/packages/compactor/src/tools/ctx-execute-file.ts +0 -5
- package/packages/compactor/src/tools/register.ts +27 -50
- package/packages/compactor/src/tools/vcc-recall.ts +86 -48
- package/packages/compactor/src/tui/settings-overlay.ts +20 -40
- package/packages/compactor/src/types.ts +43 -100
- package/packages/core/constants.ts +1 -28
- package/packages/core/events.ts +1 -83
- package/packages/core/index.ts +1 -0
- package/packages/core/model-cache.ts +18 -12
- package/packages/core/package.json +3 -1
- package/packages/core/tui-overlay.ts +89 -0
- package/packages/core/tui-width.ts +18 -0
- package/packages/core/utils.ts +43 -0
- package/packages/footer/package.json +2 -2
- package/packages/footer/src/help.ts +2 -2
- package/packages/footer/src/index.ts +9 -10
- package/packages/footer/src/presets.ts +1 -27
- package/packages/footer/src/registry/index.ts +0 -23
- package/packages/footer/src/rendering/icons.ts +0 -13
- package/packages/footer/src/rendering/renderer.ts +5 -40
- package/packages/footer/src/rendering/separators.ts +0 -10
- package/packages/footer/src/rendering/theme.ts +1 -15
- package/packages/footer/src/segments/compactor.ts +7 -14
- package/packages/footer/src/segments/core.ts +15 -54
- package/packages/footer/src/segments/kanboard.ts +5 -5
- package/packages/footer/src/segments/mcp.ts +5 -5
- package/packages/footer/src/segments/memory.ts +4 -4
- package/packages/footer/src/segments/notify.ts +2 -2
- package/packages/footer/src/segments/ralph.ts +3 -3
- package/packages/footer/src/segments/status-ext.ts +1 -1
- package/packages/footer/src/segments/workflow.ts +3 -3
- package/packages/footer/src/tps-tracker.ts +0 -8
- package/packages/footer/src/tui/settings-tui.ts +14 -31
- package/packages/footer/src/types.ts +0 -15
- package/packages/image/package.json +2 -2
- package/packages/image/src/register-providers.ts +0 -11
- package/packages/image/src/tools.ts +0 -3
- package/packages/image/src/tui/model-selector.ts +36 -71
- package/packages/info-screen/config.ts +0 -16
- package/packages/info-screen/core-groups.ts +0 -10
- package/packages/info-screen/index.ts +22 -13
- package/packages/info-screen/package.json +2 -2
- package/packages/info-screen/registry.ts +1 -72
- package/packages/info-screen/tui/info-overlay.ts +85 -100
- package/packages/info-screen/usage-parser.ts +12 -47
- package/packages/input-shortcuts/package.json +2 -2
- package/packages/input-shortcuts/src/index.ts +3 -3
- package/packages/input-shortcuts/src/registers.ts +0 -8
- package/packages/input-shortcuts/src/settings-overlay.ts +4 -13
- package/packages/input-shortcuts/src/undo-redo.ts +0 -12
- package/packages/kanboard/package.json +2 -2
- package/packages/mcp/package.json +2 -2
- package/packages/mcp/src/bridge/client.ts +0 -15
- package/packages/mcp/src/config/manager.ts +0 -38
- package/packages/mcp/src/config/schema.ts +0 -27
- package/packages/mcp/src/config/sync.ts +0 -29
- package/packages/mcp/src/index.ts +1 -1
- package/packages/mcp/src/tui/add-overlay.ts +6 -6
- package/packages/mcp/src/types.ts +0 -6
- package/packages/memory/embedding.ts +0 -31
- package/packages/memory/mempalace.ts +0 -16
- package/packages/memory/package.json +4 -8
- package/packages/memory/storage.ts +56 -753
- package/packages/milestone/hooks.ts +1 -6
- package/packages/milestone/milestone.ts +2 -41
- package/packages/milestone/package.json +2 -2
- package/packages/notify/README.md +1 -0
- package/packages/notify/commands.ts +61 -3
- package/packages/notify/events.ts +0 -10
- package/packages/notify/package.json +2 -2
- package/packages/notify/settings.ts +0 -19
- package/packages/notify/tui/gotify-setup.ts +108 -143
- package/packages/notify/tui/ntfy-setup.ts +137 -172
- package/packages/notify/tui/recap-model-selector.ts +107 -101
- package/packages/notify/tui/settings-overlay.ts +94 -117
- package/packages/notify/tui/telegram-setup.ts +48 -79
- package/packages/notify/types.ts +0 -2
- package/packages/ralph/index.ts +11 -35
- package/packages/ralph/package.json +3 -3
- package/packages/ralph/ralph-loop.ts +3 -44
- package/packages/subagents/package.json +2 -1
- package/packages/subagents/src/agent-manager.ts +0 -51
- package/packages/subagents/src/agent-runner.ts +6 -80
- package/packages/subagents/src/conversation-viewer.ts +1 -19
- package/packages/subagents/src/core-compat.ts +8 -25
- package/packages/subagents/src/custom-agents.ts +0 -1
- package/packages/subagents/src/index.ts +3 -48
- package/packages/subagents/src/types.ts +0 -11
- package/packages/subagents/src/widget.ts +5 -5
- package/packages/unipi/bundled.js +4468 -7769
- package/packages/unipi/index.ts +6 -3
- package/packages/updater/package.json +2 -2
- package/packages/updater/src/changelog.ts +1 -16
- package/packages/updater/src/index.ts +0 -5
- package/packages/updater/src/installer.ts +4 -26
- package/packages/updater/src/markdown.ts +6 -152
- package/packages/updater/src/settings.ts +0 -15
- package/packages/updater/src/tui/changelog-overlay.ts +42 -234
- package/packages/updater/src/tui/list-detail-overlay.ts +260 -0
- package/packages/updater/src/tui/readme-overlay.ts +53 -210
- package/packages/updater/src/tui/settings-overlay.ts +13 -26
- package/packages/updater/src/version.ts +2 -31
- package/packages/utility/package.json +2 -4
- package/packages/utility/src/commands.ts +4 -5
- package/packages/utility/src/herdr-sync.ts +136 -0
- package/packages/utility/src/index.ts +11 -68
- package/packages/utility/src/lifecycle/cleanup.ts +2 -16
- package/packages/utility/src/{diff/settings.ts → settings.ts} +42 -80
- package/packages/utility/src/tools/env.ts +0 -0
- package/packages/utility/src/tui/name-badge-state.ts +18 -2
- package/packages/utility/src/tui/name-badge.ts +5 -4
- package/packages/utility/src/tui/util-settings-tui.ts +41 -224
- package/packages/web-api/package.json +2 -6
- package/packages/web-api/src/engine/dependencies.ts +13 -134
- package/packages/web-api/src/engine/extract.ts +9 -99
- package/packages/web-api/src/engine/format.ts +0 -75
- package/packages/web-api/src/engine/types.ts +0 -39
- package/packages/web-api/src/index.ts +1 -2
- package/packages/web-api/src/providers/base.ts +0 -10
- package/packages/web-api/src/providers/duckduckgo.ts +0 -1
- package/packages/web-api/src/providers/firecrawl.ts +0 -9
- package/packages/web-api/src/providers/jina-reader.ts +0 -9
- package/packages/web-api/src/providers/jina-search.ts +0 -9
- package/packages/web-api/src/providers/perplexity.ts +0 -9
- package/packages/web-api/src/providers/registry.ts +0 -80
- package/packages/web-api/src/providers/serpapi.ts +0 -9
- package/packages/web-api/src/providers/tavily.ts +0 -9
- package/packages/web-api/src/providers/wigolo-client.ts +0 -30
- package/packages/web-api/src/providers/wigolo.ts +0 -1
- package/packages/web-api/src/settings.ts +17 -77
- package/packages/web-api/src/tools.ts +21 -24
- package/packages/web-api/src/tui/settings-dialog.ts +0 -1
- package/packages/workflow/commands.ts +38 -122
- package/packages/workflow/index.ts +1 -5
- package/packages/workflow/package.json +2 -2
- package/packages/cocoindex/README.md +0 -94
- package/packages/cocoindex/bridge.ts +0 -842
- package/packages/cocoindex/commands.ts +0 -175
- package/packages/cocoindex/index.ts +0 -48
- package/packages/cocoindex/installer.ts +0 -397
- package/packages/cocoindex/package.json +0 -49
- package/packages/cocoindex/skills/cocoindex/SKILL.md +0 -88
- package/packages/cocoindex/tools.ts +0 -157
- package/packages/compactor/src/display/diff-renderer.ts +0 -281
- package/packages/compactor/src/display/line-width-safety.ts +0 -28
- package/packages/compactor/src/display/render-utils.ts +0 -52
- package/packages/compactor/src/display/thinking-label.ts +0 -18
- package/packages/compactor/src/display/tool-overrides.ts +0 -136
- package/packages/compactor/src/tools/compact.ts +0 -20
- package/packages/subagents/src/file-lock.ts +0 -102
- package/packages/utility/src/cache/ttl-cache.ts +0 -311
- package/packages/utility/src/diff/highlighter.ts +0 -352
- package/packages/utility/src/diff/parser.ts +0 -191
- package/packages/utility/src/diff/renderer.ts +0 -395
- package/packages/utility/src/diff/theme.ts +0 -316
- package/packages/utility/src/diff/wrapper.ts +0 -339
- package/packages/utility/src/display/capabilities.ts +0 -214
- package/packages/utility/src/display/width.ts +0 -226
- package/packages/utility/src/tools/batch.ts +0 -229
- package/packages/utility/src/tui/badge-settings-tui.ts +0 -388
- package/packages/utility/src/tui/badge-settings.ts +0 -67
- package/packages/utility/src/tui/settings-inspector.ts +0 -303
- package/packages/web-api/src/providers/llm-summarize.ts +0 -71
|
@@ -57,23 +57,6 @@ Pause and reflect on your progress:
|
|
|
57
57
|
|
|
58
58
|
Update the task file with your reflection, then continue working.`;
|
|
59
59
|
|
|
60
|
-
/** Default task template */
|
|
61
|
-
export const DEFAULT_TEMPLATE = `# Task
|
|
62
|
-
|
|
63
|
-
Describe your task here.
|
|
64
|
-
|
|
65
|
-
## Goals
|
|
66
|
-
- Goal 1
|
|
67
|
-
- Goal 2
|
|
68
|
-
|
|
69
|
-
## Checklist
|
|
70
|
-
- [ ] Item 1
|
|
71
|
-
- [ ] Item 2
|
|
72
|
-
|
|
73
|
-
## Notes
|
|
74
|
-
(Update this as you work)
|
|
75
|
-
`;
|
|
76
|
-
|
|
77
60
|
/** Ralph loop manager */
|
|
78
61
|
export class RalphLoopManager {
|
|
79
62
|
private currentLoop: string | null = null;
|
|
@@ -144,7 +127,7 @@ export class RalphLoopManager {
|
|
|
144
127
|
if (message && this.ctx.hasUI) this.ctx.ui.notify(message, "info");
|
|
145
128
|
}
|
|
146
129
|
|
|
147
|
-
completeLoop(state: LoopState,
|
|
130
|
+
completeLoop(state: LoopState, reason: "completed" | "cancelled" = "completed", message?: string): void {
|
|
148
131
|
state.status = "completed";
|
|
149
132
|
state.completedAt = now();
|
|
150
133
|
state.active = false;
|
|
@@ -156,26 +139,7 @@ export class RalphLoopManager {
|
|
|
156
139
|
iteration: state.iteration,
|
|
157
140
|
maxIterations: state.maxIterations,
|
|
158
141
|
status: "completed",
|
|
159
|
-
reason
|
|
160
|
-
} satisfies UnipiRalphLoopEvent);
|
|
161
|
-
|
|
162
|
-
this.currentLoop = null;
|
|
163
|
-
this.updateUI();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
stopLoop(state: LoopState, message?: string): void {
|
|
167
|
-
state.status = "completed";
|
|
168
|
-
state.completedAt = now();
|
|
169
|
-
state.active = false;
|
|
170
|
-
this.saveState(state);
|
|
171
|
-
|
|
172
|
-
// Emit event
|
|
173
|
-
this.emitFn(UNIPI_EVENTS.RALPH_LOOP_END, {
|
|
174
|
-
name: state.name,
|
|
175
|
-
iteration: state.iteration,
|
|
176
|
-
maxIterations: state.maxIterations,
|
|
177
|
-
status: "completed",
|
|
178
|
-
reason: "cancelled",
|
|
142
|
+
reason,
|
|
179
143
|
} satisfies UnipiRalphLoopEvent);
|
|
180
144
|
|
|
181
145
|
this.currentLoop = null;
|
|
@@ -365,12 +329,7 @@ export class RalphLoopManager {
|
|
|
365
329
|
|
|
366
330
|
// Check max iterations
|
|
367
331
|
if (state.maxIterations > 0 && state.iteration > state.maxIterations) {
|
|
368
|
-
this.completeLoop(
|
|
369
|
-
state,
|
|
370
|
-
`───────────────────────────────────────────────────────────────────────
|
|
371
|
-
⚠️ RALPH LOOP STOPPED: ${state.name} | Max iterations (${state.maxIterations}) reached
|
|
372
|
-
───────────────────────────────────────────────────────────────────────`,
|
|
373
|
-
);
|
|
332
|
+
this.completeLoop(state);
|
|
374
333
|
return null;
|
|
375
334
|
}
|
|
376
335
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/subagents",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"description": "Subagents for UniPi — parallel execution, file locking, workflow integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"test": "npx tsx --test src/__tests__/*.test.ts"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@pi-unipi/core": "2.6.1",
|
|
21
22
|
"@earendil-works/pi-agent-core": "^0.80.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
@@ -17,7 +17,6 @@ import { loadCustomAgents } from "./custom-agents.js";
|
|
|
17
17
|
function compareCodeUnits(a: string, b: string): number {
|
|
18
18
|
return a < b ? -1 : a > b ? 1 : 0;
|
|
19
19
|
}
|
|
20
|
-
import { FileLock } from "./file-lock.js";
|
|
21
20
|
|
|
22
21
|
export type OnAgentComplete = (record: AgentRecord) => void;
|
|
23
22
|
export type OnAgentStart = (record: AgentRecord) => void;
|
|
@@ -41,7 +40,6 @@ interface SpawnOptions {
|
|
|
41
40
|
thinkingLevel?: ThinkingLevel;
|
|
42
41
|
maxTurns?: number;
|
|
43
42
|
isolated?: boolean;
|
|
44
|
-
inheritContext?: boolean;
|
|
45
43
|
isBackground?: boolean;
|
|
46
44
|
onToolActivity?: (activity: ToolActivity) => void;
|
|
47
45
|
onTextDelta?: (delta: string, fullText: string) => void;
|
|
@@ -58,9 +56,6 @@ export class AgentManager {
|
|
|
58
56
|
private customAgents: Map<string, AgentConfig>;
|
|
59
57
|
private typeSettings: SubagentsConfig["types"];
|
|
60
58
|
|
|
61
|
-
/** Per-file transparent locking for write agents. */
|
|
62
|
-
readonly fileLock = new FileLock();
|
|
63
|
-
|
|
64
59
|
/** Queue of background agents waiting to start. */
|
|
65
60
|
private queue: { id: string; args: SpawnArgs }[] = [];
|
|
66
61
|
/** Number of currently running background agents. */
|
|
@@ -107,11 +102,6 @@ export class AgentManager {
|
|
|
107
102
|
}
|
|
108
103
|
}
|
|
109
104
|
|
|
110
|
-
setMaxConcurrent(n: number) {
|
|
111
|
-
this.maxConcurrent = Math.max(1, n);
|
|
112
|
-
this.drainQueue();
|
|
113
|
-
}
|
|
114
|
-
|
|
115
105
|
getMaxConcurrent(): number {
|
|
116
106
|
return this.maxConcurrent;
|
|
117
107
|
}
|
|
@@ -140,7 +130,6 @@ export class AgentManager {
|
|
|
140
130
|
toolUses: 0,
|
|
141
131
|
startedAt: Date.now(),
|
|
142
132
|
abortController,
|
|
143
|
-
lockedFiles: new Set(),
|
|
144
133
|
};
|
|
145
134
|
this.agents.set(id, record);
|
|
146
135
|
|
|
@@ -189,7 +178,6 @@ export class AgentManager {
|
|
|
189
178
|
agentConfig,
|
|
190
179
|
maxTurns: options.maxTurns ?? agentConfig?.maxTurns,
|
|
191
180
|
isolated: options.isolated ?? agentConfig?.isolated,
|
|
192
|
-
inheritContext: options.inheritContext ?? agentConfig?.inheritContext,
|
|
193
181
|
thinkingLevel: options.thinkingLevel ?? agentConfig?.thinking,
|
|
194
182
|
signal: record.abortController!.signal,
|
|
195
183
|
onToolActivity: (activity) => {
|
|
@@ -211,10 +199,6 @@ export class AgentManager {
|
|
|
211
199
|
record.session = session;
|
|
212
200
|
record.completedAt ??= Date.now();
|
|
213
201
|
|
|
214
|
-
// Release any held file locks
|
|
215
|
-
this.fileLock.releaseAll(id);
|
|
216
|
-
record.lockedFiles.clear();
|
|
217
|
-
|
|
218
202
|
if (options.isBackground) {
|
|
219
203
|
this.runningBackground--;
|
|
220
204
|
this.onComplete?.(record);
|
|
@@ -229,10 +213,6 @@ export class AgentManager {
|
|
|
229
213
|
record.error = err instanceof Error ? err.message : String(err);
|
|
230
214
|
record.completedAt ??= Date.now();
|
|
231
215
|
|
|
232
|
-
// Release any held file locks
|
|
233
|
-
this.fileLock.releaseAll(id);
|
|
234
|
-
record.lockedFiles.clear();
|
|
235
|
-
|
|
236
216
|
if (options.isBackground) {
|
|
237
217
|
this.runningBackground--;
|
|
238
218
|
this.onComplete?.(record);
|
|
@@ -293,8 +273,6 @@ export class AgentManager {
|
|
|
293
273
|
record.abortController?.abort();
|
|
294
274
|
record.status = "stopped";
|
|
295
275
|
record.completedAt = Date.now();
|
|
296
|
-
this.fileLock.releaseAll(id);
|
|
297
|
-
record.lockedFiles.clear();
|
|
298
276
|
return true;
|
|
299
277
|
}
|
|
300
278
|
|
|
@@ -318,38 +296,9 @@ export class AgentManager {
|
|
|
318
296
|
count++;
|
|
319
297
|
}
|
|
320
298
|
}
|
|
321
|
-
this.fileLock.clear();
|
|
322
299
|
return count;
|
|
323
300
|
}
|
|
324
301
|
|
|
325
|
-
/** Wait for all agents. */
|
|
326
|
-
async waitForAll(): Promise<void> {
|
|
327
|
-
while (true) {
|
|
328
|
-
this.drainQueue();
|
|
329
|
-
const pending = [...this.agents.values()]
|
|
330
|
-
.filter((r) => r.status === "running" || r.status === "queued")
|
|
331
|
-
.map((r) => r.promise)
|
|
332
|
-
.filter(Boolean);
|
|
333
|
-
if (pending.length === 0) break;
|
|
334
|
-
await Promise.allSettled(pending);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/** Whether any agents running or queued. */
|
|
339
|
-
hasRunning(): boolean {
|
|
340
|
-
return [...this.agents.values()].some((r) => r.status === "running" || r.status === "queued");
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/** Remove completed records. */
|
|
344
|
-
clearCompleted(): void {
|
|
345
|
-
for (const [id, record] of this.agents) {
|
|
346
|
-
if (record.status === "running" || record.status === "queued") continue;
|
|
347
|
-
record.session?.dispose?.();
|
|
348
|
-
record.session = undefined;
|
|
349
|
-
this.agents.delete(id);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
302
|
private cleanup() {
|
|
354
303
|
const cutoff = Date.now() - 10 * 60_000;
|
|
355
304
|
for (const [id, record] of this.agents) {
|
|
@@ -25,27 +25,8 @@ const EXCLUDED_TOOL_NAMES = ["spawn_helper", "get_helper_result", "Agent", "get_
|
|
|
25
25
|
/** All known built-in tool names. */
|
|
26
26
|
const BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export function getDefaultMaxTurns(): number | undefined {
|
|
32
|
-
return defaultMaxTurns;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function setDefaultMaxTurns(n: number | undefined): void {
|
|
36
|
-
defaultMaxTurns = n == null || n === 0 ? undefined : Math.max(1, n);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Grace turns after soft limit. */
|
|
40
|
-
let graceTurns = 5;
|
|
41
|
-
|
|
42
|
-
export function getGraceTurns(): number {
|
|
43
|
-
return graceTurns;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function setGraceTurns(n: number): void {
|
|
47
|
-
graceTurns = Math.max(1, n);
|
|
48
|
-
}
|
|
28
|
+
/** Grace turns after the soft turn limit before aborting. */
|
|
29
|
+
const GRACE_TURNS = 5;
|
|
49
30
|
|
|
50
31
|
/** Tool activity info. */
|
|
51
32
|
export interface ToolActivity {
|
|
@@ -61,7 +42,6 @@ export interface RunOptions {
|
|
|
61
42
|
maxTurns?: number;
|
|
62
43
|
signal?: AbortSignal;
|
|
63
44
|
isolated?: boolean;
|
|
64
|
-
inheritContext?: boolean;
|
|
65
45
|
thinkingLevel?: ThinkingLevel;
|
|
66
46
|
cwd?: string;
|
|
67
47
|
onToolActivity?: (activity: ToolActivity) => void;
|
|
@@ -123,8 +103,7 @@ function getToolNamesForType(type: AgentType, config?: AgentConfig): string[] {
|
|
|
123
103
|
return [...BUILTIN_TOOL_NAMES];
|
|
124
104
|
}
|
|
125
105
|
|
|
126
|
-
/**
|
|
127
|
-
* Resolve agent config for a type.
|
|
106
|
+
/** Resolve agent config for a type.
|
|
128
107
|
* Priority: explicit config > builtin config > default
|
|
129
108
|
*/
|
|
130
109
|
function resolveAgentConfig(type: AgentType, explicitConfig?: AgentConfig): AgentConfig | undefined {
|
|
@@ -132,15 +111,6 @@ function resolveAgentConfig(type: AgentType, explicitConfig?: AgentConfig): Agen
|
|
|
132
111
|
return BUILTIN_CONFIGS[type];
|
|
133
112
|
}
|
|
134
113
|
|
|
135
|
-
/** Resolve model from config. */
|
|
136
|
-
function resolveDefaultModel(
|
|
137
|
-
parentModel: Model<any> | undefined,
|
|
138
|
-
configModel?: string,
|
|
139
|
-
): Model<any> | undefined {
|
|
140
|
-
// For now, just use parent model. Full model resolution requires registry.
|
|
141
|
-
return parentModel;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
114
|
/**
|
|
145
115
|
* Run an agent session.
|
|
146
116
|
*/
|
|
@@ -191,7 +161,7 @@ export async function runAgent(
|
|
|
191
161
|
await loader.reload();
|
|
192
162
|
|
|
193
163
|
// Resolve model
|
|
194
|
-
const model = options.model ??
|
|
164
|
+
const model = options.model ?? ctx.model;
|
|
195
165
|
|
|
196
166
|
// Create session
|
|
197
167
|
const sessionOpts: Parameters<typeof createAgentSession>[0] = {
|
|
@@ -235,7 +205,7 @@ export async function runAgent(
|
|
|
235
205
|
|
|
236
206
|
// Track turns
|
|
237
207
|
let turnCount = 0;
|
|
238
|
-
const maxTurns = options.maxTurns
|
|
208
|
+
const maxTurns = options.maxTurns;
|
|
239
209
|
let softLimitReached = false;
|
|
240
210
|
let aborted = false;
|
|
241
211
|
|
|
@@ -248,7 +218,7 @@ export async function runAgent(
|
|
|
248
218
|
if (!softLimitReached && turnCount >= maxTurns) {
|
|
249
219
|
softLimitReached = true;
|
|
250
220
|
session.steer("You have reached your turn limit. Wrap up immediately — provide your final answer now.");
|
|
251
|
-
} else if (softLimitReached && turnCount >= maxTurns +
|
|
221
|
+
} else if (softLimitReached && turnCount >= maxTurns + GRACE_TURNS) {
|
|
252
222
|
aborted = true;
|
|
253
223
|
session.abort();
|
|
254
224
|
}
|
|
@@ -283,47 +253,3 @@ export async function runAgent(
|
|
|
283
253
|
const responseText = collector.getText().trim() || getLastAssistantText(session);
|
|
284
254
|
return { responseText, session, aborted, steered: softLimitReached };
|
|
285
255
|
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Get conversation text from a session.
|
|
289
|
-
*/
|
|
290
|
-
export function getAgentConversation(session: AgentSession): string {
|
|
291
|
-
const parts: string[] = [];
|
|
292
|
-
|
|
293
|
-
for (const msg of session.messages) {
|
|
294
|
-
if (msg.role === "user") {
|
|
295
|
-
const content = msg.content;
|
|
296
|
-
const text = typeof content === "string"
|
|
297
|
-
? content
|
|
298
|
-
: content
|
|
299
|
-
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
300
|
-
.map((c) => c.text)
|
|
301
|
-
.join("");
|
|
302
|
-
if (text.trim()) parts.push(`[User]: ${text.trim()}`);
|
|
303
|
-
} else if (msg.role === "assistant") {
|
|
304
|
-
const textParts: string[] = [];
|
|
305
|
-
const toolCalls: string[] = [];
|
|
306
|
-
const content = msg.content;
|
|
307
|
-
if (typeof content !== "string") {
|
|
308
|
-
for (const c of content) {
|
|
309
|
-
if (c.type === "text" && c.text) textParts.push(c.text);
|
|
310
|
-
else if (c.type === "toolCall") toolCalls.push(` Tool: ${(c as any).name ?? "unknown"}`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
if (textParts.length > 0) parts.push(`[Assistant]: ${textParts.join("\n")}`);
|
|
314
|
-
if (toolCalls.length > 0) parts.push(`[Tool Calls]:\n${toolCalls.join("\n")}`);
|
|
315
|
-
} else if (msg.role === "toolResult") {
|
|
316
|
-
const content = msg.content;
|
|
317
|
-
const text = typeof content === "string"
|
|
318
|
-
? content
|
|
319
|
-
: content
|
|
320
|
-
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
|
321
|
-
.map((c) => c.text)
|
|
322
|
-
.join("");
|
|
323
|
-
const truncated = text.length > 200 ? text.slice(0, 200) + "..." : text;
|
|
324
|
-
parts.push(`[Tool Result (${msg.toolName})]: ${truncated}`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return parts.join("\n\n");
|
|
329
|
-
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { formatMs, describeActivity } from "./widget.js";
|
|
10
11
|
import {
|
|
11
12
|
type Component,
|
|
12
13
|
matchesKey,
|
|
@@ -30,13 +31,6 @@ function extractText(content: string | Array<{ type: string; text?: string }>):
|
|
|
30
31
|
.join("");
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
/** Format duration. */
|
|
34
|
-
function formatMs(ms: number): string {
|
|
35
|
-
if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
|
|
36
|
-
if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
|
|
37
|
-
return `${ms}ms`;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
34
|
/** Format tokens compactly. */
|
|
41
35
|
function formatTokens(count: number): string {
|
|
42
36
|
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M token`;
|
|
@@ -45,18 +39,6 @@ function formatTokens(count: number): string {
|
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
/** Describe current activity from active tools. */
|
|
48
|
-
function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
|
|
49
|
-
if (activeTools.size > 0) {
|
|
50
|
-
const names = [...new Set(activeTools.values())];
|
|
51
|
-
return names.join(", ") + "…";
|
|
52
|
-
}
|
|
53
|
-
if (responseText && responseText.trim().length > 0) {
|
|
54
|
-
const lastLine = responseText.split("\n").find((l) => l.trim())?.trim() ?? "";
|
|
55
|
-
if (lastLine.length > 60) return lastLine.slice(0, 60) + "…";
|
|
56
|
-
if (lastLine.length > 0) return lastLine;
|
|
57
|
-
}
|
|
58
|
-
return "thinking…";
|
|
59
|
-
}
|
|
60
42
|
|
|
61
43
|
interface ViewerRecord {
|
|
62
44
|
type: string;
|
|
@@ -1,33 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/subagents — Local helpers (not in @pi-unipi/core)
|
|
3
|
+
*
|
|
4
|
+
* boundHelperOutput: bounded output with artifact-to-disk fallback.
|
|
5
|
+
* withHerdrBlocked: wraps a fn with herdr:blocked events.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { emitEvent } from "@pi-unipi/core";
|
|
1
9
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
10
|
import { chmodSync, lstatSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
11
|
import { homedir } from "node:os";
|
|
4
12
|
import { join } from "node:path";
|
|
5
13
|
import { randomUUID } from "node:crypto";
|
|
6
14
|
|
|
7
|
-
export const MODULES = { SUBAGENTS: "subagents" } as const;
|
|
8
|
-
|
|
9
|
-
export const UNIPI_EVENTS = {
|
|
10
|
-
MODULE_READY: "unipi:module:ready",
|
|
11
|
-
BADGE_GENERATE_REQUEST: "unipi:badge:generate:request",
|
|
12
|
-
} as const;
|
|
13
|
-
|
|
14
|
-
export interface UnipiBadgeGenerateRequestEvent {
|
|
15
|
-
conversationSummary?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function emitEvent(
|
|
19
|
-
pi: Pick<ExtensionAPI, "events">,
|
|
20
|
-
eventName: string,
|
|
21
|
-
payload: unknown,
|
|
22
|
-
): boolean {
|
|
23
|
-
try {
|
|
24
|
-
pi.events.emit(eventName, payload);
|
|
25
|
-
return true;
|
|
26
|
-
} catch {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
15
|
export interface BoundedHelperOutput {
|
|
32
16
|
text: string;
|
|
33
17
|
truncated: boolean;
|
|
@@ -35,7 +19,6 @@ export interface BoundedHelperOutput {
|
|
|
35
19
|
artifactPath?: string;
|
|
36
20
|
}
|
|
37
21
|
|
|
38
|
-
/** Standalone-package twin of core's bounded-output policy. */
|
|
39
22
|
const MAX_RAW_HELPER_ARTIFACT_BYTES = 16 * 1024 * 1024;
|
|
40
23
|
|
|
41
24
|
export function boundHelperOutput(
|
|
@@ -74,7 +74,6 @@ function loadAgentFromFile(filePath: string, source: "project" | "global"): Agen
|
|
|
74
74
|
maxTurns: (frontmatter as any).max_turns as number | undefined,
|
|
75
75
|
systemPrompt: body.trim(),
|
|
76
76
|
promptMode: ((frontmatter as any).prompt_mode as "replace" | "append") ?? "replace",
|
|
77
|
-
inheritContext: (frontmatter as any).inherit_context as boolean | undefined,
|
|
78
77
|
runInBackground: (frontmatter as any).run_in_background as boolean | undefined,
|
|
79
78
|
isolated: (frontmatter as any).isolated as boolean | undefined,
|
|
80
79
|
enabled: (frontmatter as any).enabled !== false,
|
|
@@ -12,12 +12,13 @@ import { Type } from "typebox";
|
|
|
12
12
|
import { existsSync, readdirSync } from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
|
-
import {
|
|
15
|
+
import { MODULES, UNIPI_EVENTS, emitEvent, type UnipiBadgeGenerateRequestEvent } from "@pi-unipi/core";
|
|
16
|
+
import { boundHelperOutput, withHerdrBlocked } from "./core-compat.js";
|
|
16
17
|
import { AgentManager } from "./agent-manager.js";
|
|
17
18
|
import { initConfig } from "./config.js";
|
|
18
19
|
import { type AgentActivity, type NotificationDetails, BUILTIN_TYPES } from "./types.js";
|
|
19
20
|
import { ConversationViewer } from "./conversation-viewer.js";
|
|
20
|
-
import { AgentWidget } from "./widget.js";
|
|
21
|
+
import { AgentWidget, SPINNER, TOOL_DISPLAY, formatMs, formatTurns, describeActivity } from "./widget.js";
|
|
21
22
|
|
|
22
23
|
/** Get info registry from global */
|
|
23
24
|
function getInfoRegistry() {
|
|
@@ -26,18 +27,8 @@ function getInfoRegistry() {
|
|
|
26
27
|
|
|
27
28
|
// ---- Formatting helpers (shared between renderers and inline text) ----
|
|
28
29
|
|
|
29
|
-
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
30
30
|
|
|
31
31
|
/** Tool name → human-readable action. */
|
|
32
|
-
const TOOL_DISPLAY: Record<string, string> = {
|
|
33
|
-
read: "reading",
|
|
34
|
-
bash: "running command",
|
|
35
|
-
edit: "editing",
|
|
36
|
-
write: "writing",
|
|
37
|
-
grep: "searching",
|
|
38
|
-
find: "finding files",
|
|
39
|
-
ls: "listing",
|
|
40
|
-
};
|
|
41
32
|
|
|
42
33
|
function formatTokens(count: number): string {
|
|
43
34
|
if (count >= 1_000_000) return `${(count / 1_000_000).toFixed(1)}M token`;
|
|
@@ -45,42 +36,6 @@ function formatTokens(count: number): string {
|
|
|
45
36
|
return `${count} token`;
|
|
46
37
|
}
|
|
47
38
|
|
|
48
|
-
function formatTurns(turn: number, max?: number | null): string {
|
|
49
|
-
return max != null ? `⟳${turn}≤${max}` : `⟳${turn}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function formatMs(ms: number): string {
|
|
53
|
-
if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
|
|
54
|
-
if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
|
|
55
|
-
return `${ms}ms`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** Build activity description from active tools. */
|
|
59
|
-
function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
|
|
60
|
-
if (activeTools.size > 0) {
|
|
61
|
-
const groups = new Map<string, number>();
|
|
62
|
-
for (const toolName of activeTools.values()) {
|
|
63
|
-
const action = TOOL_DISPLAY[toolName] ?? toolName;
|
|
64
|
-
groups.set(action, (groups.get(action) ?? 0) + 1);
|
|
65
|
-
}
|
|
66
|
-
const parts: string[] = [];
|
|
67
|
-
for (const [action, count] of groups) {
|
|
68
|
-
if (count > 1) {
|
|
69
|
-
parts.push(`${action} ${count} ${action === "searching" ? "patterns" : "files"}`);
|
|
70
|
-
} else {
|
|
71
|
-
parts.push(action);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return parts.join(", ") + "…";
|
|
75
|
-
}
|
|
76
|
-
if (responseText && responseText.trim().length > 0) {
|
|
77
|
-
const line = responseText.split("\n").find((l) => l.trim())?.trim() ?? "";
|
|
78
|
-
if (line.length > 60) return line.slice(0, 60) + "…";
|
|
79
|
-
if (line.length > 0) return line;
|
|
80
|
-
}
|
|
81
|
-
return "thinking…";
|
|
82
|
-
}
|
|
83
|
-
|
|
84
39
|
/** Format tokens safely from session. */
|
|
85
40
|
function safeFormatTokens(session: any): string {
|
|
86
41
|
if (!session) return "";
|
|
@@ -74,7 +74,6 @@ export interface AgentConfig {
|
|
|
74
74
|
maxTurns?: number;
|
|
75
75
|
systemPrompt: string;
|
|
76
76
|
promptMode: "replace" | "append";
|
|
77
|
-
inheritContext?: boolean;
|
|
78
77
|
runInBackground?: boolean;
|
|
79
78
|
isolated?: boolean;
|
|
80
79
|
memory?: MemoryScope;
|
|
@@ -101,16 +100,6 @@ export interface AgentRecord {
|
|
|
101
100
|
promise?: Promise<string>;
|
|
102
101
|
/** Set when result consumed via get_result — suppresses notification. */
|
|
103
102
|
resultConsumed?: boolean;
|
|
104
|
-
/** Files locked by this agent. */
|
|
105
|
-
lockedFiles: Set<string>;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** File lock entry. */
|
|
109
|
-
export interface FileLockEntry {
|
|
110
|
-
agentId: string;
|
|
111
|
-
filePath: string;
|
|
112
|
-
promise: Promise<void>;
|
|
113
|
-
release: () => void;
|
|
114
103
|
}
|
|
115
104
|
|
|
116
105
|
/** Extension config. */
|
|
@@ -21,13 +21,13 @@ import type { AgentActivity } from "./types.js";
|
|
|
21
21
|
const MAX_WIDGET_LINES = 12;
|
|
22
22
|
|
|
23
23
|
/** Braille spinner frames. */
|
|
24
|
-
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
24
|
+
export const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
25
25
|
|
|
26
26
|
/** Statuses that indicate error/non-success (for linger behavior). */
|
|
27
27
|
const ERROR_STATUSES = new Set(["error", "aborted", "stopped"]);
|
|
28
28
|
|
|
29
29
|
/** Tool name → human-readable action for activity descriptions. */
|
|
30
|
-
const TOOL_DISPLAY: Record<string, string> = {
|
|
30
|
+
export const TOOL_DISPLAY: Record<string, string> = {
|
|
31
31
|
read: "reading",
|
|
32
32
|
bash: "running command",
|
|
33
33
|
edit: "editing",
|
|
@@ -40,14 +40,14 @@ const TOOL_DISPLAY: Record<string, string> = {
|
|
|
40
40
|
// ---- Formatting helpers ----
|
|
41
41
|
|
|
42
42
|
/** Format duration. */
|
|
43
|
-
function formatMs(ms: number): string {
|
|
43
|
+
export function formatMs(ms: number): string {
|
|
44
44
|
if (ms >= 60_000) return `${(ms / 60_000).toFixed(1)}m`;
|
|
45
45
|
if (ms >= 1_000) return `${(ms / 1_000).toFixed(1)}s`;
|
|
46
46
|
return `${ms}ms`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/** Format turns with optional max limit: "⟳5≤30" or "⟳5". */
|
|
50
|
-
function formatTurns(turn: number, max?: number): string {
|
|
50
|
+
export function formatTurns(turn: number, max?: number): string {
|
|
51
51
|
return max != null ? `⟳${turn}≤${max}` : `⟳${turn}`;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ function formatTurns(turn: number, max?: number): string {
|
|
|
55
55
|
* Build a human-readable activity string from currently-running tools.
|
|
56
56
|
* Groups by tool type with counts: "reading 3 files, searching 2 patterns".
|
|
57
57
|
*/
|
|
58
|
-
function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
|
|
58
|
+
export function describeActivity(activeTools: Map<string, string>, responseText?: string): string {
|
|
59
59
|
if (activeTools.size > 0) {
|
|
60
60
|
const groups = new Map<string, number>();
|
|
61
61
|
for (const toolName of activeTools.values()) {
|