@evandu/super-pi 0.1.164 → 0.1.166
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/agents/coder.md +13 -1
- package/agents/critic.md +6 -2
- package/agents/patcher.md +6 -2
- package/agents/planner.md +14 -2
- package/agents/project-leader.md +25 -0
- package/agents/reviewer.md +13 -1
- package/agents/scout.md +6 -2
- package/agents/super-pi-task/core.ts +11 -7
- package/agents/verifier.md +6 -2
- package/dist/client/.super-pi-client-sources.json +10 -8
- package/dist/client/assets/{CodeMirrorEditor-B7hhyG33.js → CodeMirrorEditor-q5ckKYpW.js} +2 -2
- package/dist/client/assets/{dist-CdpuBRoy.js → dist-3Qp_-Z3t.js} +1 -1
- package/dist/client/assets/{dist-B5xG-jTK.js → dist-B61EUjUz.js} +1 -1
- package/dist/client/assets/{dist-BLhPsVFt.js → dist-BN9HZG4O.js} +1 -1
- package/dist/client/assets/{dist-CxZb6ATc.js → dist-BOU0i9JW.js} +1 -1
- package/dist/client/assets/{dist-BBfDBMAk.js → dist-BT3fZ0wT.js} +1 -1
- package/dist/client/assets/{dist-BDy5Nw4_.js → dist-B_l0KBjV.js} +1 -1
- package/dist/client/assets/{dist-Foaz_oK2.js → dist-Bb-gT7aj.js} +1 -1
- package/dist/client/assets/{dist-D607J8oC.js → dist-CBqBGuBl.js} +1 -1
- package/dist/client/assets/{dist-CGO5Bn1x.js → dist-CXXAxAE4.js} +1 -1
- package/dist/client/assets/{dist-DJ4sWDSy.js → dist-Crew6BC1.js} +1 -1
- package/dist/client/assets/{dist-BjlmPyRl.js → dist-Cs1P7JrO.js} +1 -1
- package/dist/client/assets/{dist-CsBiEr54.js → dist-CyTiEqGG.js} +1 -1
- package/dist/client/assets/{dist-7Nz91m3a.js → dist-D10kcMrw.js} +1 -1
- package/dist/client/assets/{dist-D6nsZZd3.js → dist-DMynScoj.js} +1 -1
- package/dist/client/assets/{dist-CrJV_eES.js → dist-DRHWMjx_.js} +1 -1
- package/dist/client/assets/{dist-qqDS87aN.js → dist-DSIy9Ers.js} +1 -1
- package/dist/client/assets/{dist-BmeBkDNB.js → dist-DdfZJR8e.js} +1 -1
- package/dist/client/assets/{dist-CF9zuubZ.js → dist-DiqGp2ie.js} +1 -1
- package/dist/client/assets/{dist-ByEzPyEi.js → dist-YFQ3ixZU.js} +1 -1
- package/dist/client/assets/{dist-DaUyp_NN.js → dist-_pAt4bRw.js} +1 -1
- package/dist/client/assets/{dist-BhouAfNI.js → dist-bLHDT2q5.js} +1 -1
- package/dist/client/assets/{dist-C4ql8ak8.js → dist-dbLsO_41.js} +1 -1
- package/dist/client/assets/{index-Wtlj0s4R.css → index-C6YWfUu2.css} +1 -1
- package/dist/client/assets/{index-1hS3wVqF.js → index-dARLbQ-8.js} +19 -19
- package/dist/client/index.html +2 -2
- package/dist/server/agents.d.ts +11 -0
- package/dist/server/agents.js +50 -6
- package/dist/server/application/project-service.d.ts +95 -4
- package/dist/server/application/project-service.js +328 -72
- package/dist/server/http-server.js +7 -0
- package/dist/server/store.d.ts +17 -0
- package/dist/server/store.js +97 -1
- package/dist/server/subagent-runs.js +19 -1
- package/dist/server/task-tool.d.ts +1 -1
- package/dist/server/task-tool.js +4 -3
- package/dist/server/types.d.ts +44 -1
- package/package.json +11 -2
package/dist/server/store.d.ts
CHANGED
|
@@ -924,6 +924,23 @@ export declare class Store {
|
|
|
924
924
|
* as durable as the conversation it belongs to.
|
|
925
925
|
*/
|
|
926
926
|
setSubagentRun(runId: string, changes: SubagentRunUpdate): Promise<SubagentRun>;
|
|
927
|
+
/**
|
|
928
|
+
* Adds ONE settled run's usage to the ledger of the conversation that dispatched it.
|
|
929
|
+
*
|
|
930
|
+
* CALLED AT THE SETTLE, ONCE PER RUN, and the run's FINAL accumulated usage is what it reads - the
|
|
931
|
+
* run record already holds the total of every finished message the child produced, so nothing is
|
|
932
|
+
* re-derived here and no message is counted twice.
|
|
933
|
+
*
|
|
934
|
+
* THE FLAG AND THE LEDGER ENTRY ARE WRITTEN IN ONE WRITE, and that is the whole crash-safety
|
|
935
|
+
* argument: `foldRunUsage` mutates both in the same synchronous block and this persists the state
|
|
936
|
+
* once, so a server that dies here cannot leave the money counted twice (flag written, entry not)
|
|
937
|
+
* or not at all (entry written, flag not). A run that was folded already is left alone, so calling
|
|
938
|
+
* this twice is a no-op rather than a second bill.
|
|
939
|
+
*
|
|
940
|
+
* IT PERSISTS THE WHOLE STATE rather than the two rows, because the two rows ARE one fact: the
|
|
941
|
+
* run's flag and the conversation's ledger entry have to become true together or not at all.
|
|
942
|
+
*/
|
|
943
|
+
accountSubagentUsage(runId: string): Promise<boolean>;
|
|
927
944
|
/**
|
|
928
945
|
* Replaces the model's task list, and persists it.
|
|
929
946
|
*
|
package/dist/server/store.js
CHANGED
|
@@ -105,6 +105,71 @@ function reconcileSubagentRuns(state) {
|
|
|
105
105
|
}
|
|
106
106
|
return orphaned;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* ONE RUN'S USAGE, FOLDED INTO THE LEDGER OF THE CONVERSATION THAT DISPATCHED IT.
|
|
110
|
+
*
|
|
111
|
+
* WHY THE CHILD'S MONEY BELONGS TO THE PARENT'S LEDGER. A one-shot call spends real money on a
|
|
112
|
+
* child Pi, and the only number a budget is compared against is a conversation's `usage`. A ledger
|
|
113
|
+
* that held the parent's own turns alone would let a conversation multiply its real cost past its
|
|
114
|
+
* own cap simply by dispatching children instead of answering - which is precisely what design §5
|
|
115
|
+
* exists to prevent ("写预算的人必须能自己算出这个数是多少钱"). So the child's FINAL accumulated
|
|
116
|
+
* usage is added to the parent's, once, when the run settles.
|
|
117
|
+
*
|
|
118
|
+
* ONCE, AND ONLY ONCE, AND WHY THE FLAG IS THE WHOLE MECHANISM. A run reports its usage on every
|
|
119
|
+
* finished assistant message, and the recorder settles the run with the run's TOTAL - so folding
|
|
120
|
+
* per message would bill every message twice. The fold therefore happens once, on the settled run,
|
|
121
|
+
* and `usageAccounted` says which side of that line the run is on. The flag and the ledger entry
|
|
122
|
+
* are written in ONE store write, so a server that dies between them cannot leave the money
|
|
123
|
+
* counted twice or not at all.
|
|
124
|
+
*
|
|
125
|
+
* THE FIELDS ARE ADDED, NOT RE-DERIVED. A conversation's usage and a run's usage are the same
|
|
126
|
+
* shape (`input` / `output` / `cacheRead` / `cacheWrite` / `cost` / `turns`), so the sum is a sum -
|
|
127
|
+
* no second pricing formula, and no place where the two could disagree about what a token costs.
|
|
128
|
+
*
|
|
129
|
+
* A RUN WITH NO CONVERSATION IS NOT FOLDED, and is not an error: the conversation it belonged to
|
|
130
|
+
* was deleted while the child was running, so there is no ledger left to add to. Deleting a
|
|
131
|
+
* conversation deletes its runs too, so the money is not counted twice either - it is simply not
|
|
132
|
+
* counted, because nobody is left to be billed for it.
|
|
133
|
+
*/
|
|
134
|
+
function foldRunUsage(state, run) {
|
|
135
|
+
if (!run?.usage || run.usageAccounted)
|
|
136
|
+
return false;
|
|
137
|
+
const conversation = (state.conversations ?? []).find((item) => item?.id === run.conversationId);
|
|
138
|
+
if (!conversation)
|
|
139
|
+
return false;
|
|
140
|
+
const previous = conversation.usage ?? { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
141
|
+
conversation.usage = {
|
|
142
|
+
input: previous.input + run.usage.input,
|
|
143
|
+
output: previous.output + run.usage.output,
|
|
144
|
+
cacheRead: previous.cacheRead + run.usage.cacheRead,
|
|
145
|
+
cacheWrite: previous.cacheWrite + run.usage.cacheWrite,
|
|
146
|
+
cost: previous.cost + run.usage.cost,
|
|
147
|
+
turns: previous.turns + run.usage.turns,
|
|
148
|
+
};
|
|
149
|
+
conversation.usageUpdatedAt = now();
|
|
150
|
+
run.usageAccounted = true;
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* THE STARTUP SWEEP FOR RUNS THAT PREDATE THE LEDGER.
|
|
155
|
+
*
|
|
156
|
+
* A `state.json` written before the conversation ledger existed holds runs whose usage was never
|
|
157
|
+
* added to the conversation that dispatched them - because at the time, nothing did that. Left
|
|
158
|
+
* alone, the project's budget would silently DROP for every project that had already spent money
|
|
159
|
+
* on one-shot calls, which is the kind of change nobody can explain afterwards.
|
|
160
|
+
*
|
|
161
|
+
* So every run that carries usage and has not been accounted for is folded in here, once, on the
|
|
162
|
+
* next boot. The flag makes the sweep idempotent: a run folded by this sweep is flagged, and the
|
|
163
|
+
* next boot leaves it alone. Mutated in place, like every other sweep in this file, and persisted
|
|
164
|
+
* by the single write `open` performs when this reports a change.
|
|
165
|
+
*/
|
|
166
|
+
function foldUnaccountedRunUsage(state) {
|
|
167
|
+
let folded = 0;
|
|
168
|
+
for (const run of state.subagentRuns ?? [])
|
|
169
|
+
if (foldRunUsage(state, run))
|
|
170
|
+
folded += 1;
|
|
171
|
+
return folded;
|
|
172
|
+
}
|
|
108
173
|
/**
|
|
109
174
|
* Why a conversation the previous process left `running` is failed, in the same spirit and for the same
|
|
110
175
|
* audience as `ORPHANED_RUN_ERROR`: this lands in a field the chat pane renders verbatim, so it names the
|
|
@@ -634,11 +699,15 @@ export class Store {
|
|
|
634
699
|
const interrupted = reconcileConversations(state);
|
|
635
700
|
const requeued = reconcileWaitingMessages(state);
|
|
636
701
|
const todos = reconcileTodos(state);
|
|
702
|
+
// THE USAGE SWEEP. A run recorded before the conversation ledger existed has its money sitting in
|
|
703
|
+
// the run record and nowhere else, so the ledger is brought up to date here - once, and only
|
|
704
|
+
// for the runs that carry usage and have not been accounted for.
|
|
705
|
+
const foldedUsage = foldUnaccountedRunUsage(state);
|
|
637
706
|
const store = new Store(repository, normalizeMessageTimestamps(state));
|
|
638
707
|
// Persisted here, in `open`, rather than left for the first unrelated write: a reconciled record that
|
|
639
708
|
// is only rewritten by a later mutation would still read `running` to anything that opened the file
|
|
640
709
|
// in the meantime, and would be reconciled a SECOND time on the next restart.
|
|
641
|
-
if (defaulted || orphaned.length || orphanedWorkflows.length || orphanedTasks.changed || interrupted.length || requeued.recovered.length || requeued.changed || todos.changed) {
|
|
710
|
+
if (defaulted || orphaned.length || orphanedWorkflows.length || orphanedTasks.changed || interrupted.length || requeued.recovered.length || requeued.changed || todos.changed || foldedUsage) {
|
|
642
711
|
for (const run of orphaned)
|
|
643
712
|
console.warn(`Super Pi: sub-agent run ${run.id} was still marked running when the last server stopped - it is now failed, because no child process survived it.`);
|
|
644
713
|
for (const plan of orphanedWorkflows)
|
|
@@ -660,6 +729,10 @@ export class Store {
|
|
|
660
729
|
console.warn(`Super Pi: message ${entry.message.id} of conversation ${entry.conversationId} was still marked sending when the last server stopped - it is waiting again and cancellable, because the Pi that would have delivered it is gone.`);
|
|
661
730
|
for (const entry of requeued.folded)
|
|
662
731
|
console.warn(`Super Pi: message ${entry.message.id} of conversation ${entry.conversationId} was waiting in the old queue format - it has been moved into that conversation's transcript.`);
|
|
732
|
+
// Announced, because a budget that silently changed shape is a budget nobody can explain: this
|
|
733
|
+
// says the money those runs spent is now part of the conversations that dispatched them.
|
|
734
|
+
if (foldedUsage)
|
|
735
|
+
console.warn(`Super Pi: the usage of ${foldedUsage} sub-agent run(s) recorded before the usage ledger existed was added to the conversations that dispatched them.`);
|
|
663
736
|
await store.persist();
|
|
664
737
|
}
|
|
665
738
|
return store;
|
|
@@ -2542,6 +2615,29 @@ export class Store {
|
|
|
2542
2615
|
await this.persistTransaction({ kind: "subagent.replace", run: structuredClone(run) });
|
|
2543
2616
|
return structuredClone(run);
|
|
2544
2617
|
}
|
|
2618
|
+
/**
|
|
2619
|
+
* Adds ONE settled run's usage to the ledger of the conversation that dispatched it.
|
|
2620
|
+
*
|
|
2621
|
+
* CALLED AT THE SETTLE, ONCE PER RUN, and the run's FINAL accumulated usage is what it reads - the
|
|
2622
|
+
* run record already holds the total of every finished message the child produced, so nothing is
|
|
2623
|
+
* re-derived here and no message is counted twice.
|
|
2624
|
+
*
|
|
2625
|
+
* THE FLAG AND THE LEDGER ENTRY ARE WRITTEN IN ONE WRITE, and that is the whole crash-safety
|
|
2626
|
+
* argument: `foldRunUsage` mutates both in the same synchronous block and this persists the state
|
|
2627
|
+
* once, so a server that dies here cannot leave the money counted twice (flag written, entry not)
|
|
2628
|
+
* or not at all (entry written, flag not). A run that was folded already is left alone, so calling
|
|
2629
|
+
* this twice is a no-op rather than a second bill.
|
|
2630
|
+
*
|
|
2631
|
+
* IT PERSISTS THE WHOLE STATE rather than the two rows, because the two rows ARE one fact: the
|
|
2632
|
+
* run's flag and the conversation's ledger entry have to become true together or not at all.
|
|
2633
|
+
*/
|
|
2634
|
+
async accountSubagentUsage(runId) {
|
|
2635
|
+
const run = this.state.subagentRuns.find((item) => item.id === runId);
|
|
2636
|
+
if (!foldRunUsage(this.state, run))
|
|
2637
|
+
return false;
|
|
2638
|
+
await this.persist();
|
|
2639
|
+
return true;
|
|
2640
|
+
}
|
|
2545
2641
|
/**
|
|
2546
2642
|
* Replaces the model's task list, and persists it.
|
|
2547
2643
|
*
|
|
@@ -305,7 +305,7 @@ export class SubagentRunRecorder {
|
|
|
305
305
|
// `output`; the streamed copy is only a fallback for a child that never sent a `done`.
|
|
306
306
|
const output = result.finalText || streamed;
|
|
307
307
|
const truncated = output.length > PER_TASK_OUTPUT_CAP;
|
|
308
|
-
|
|
308
|
+
const settled = await settle({
|
|
309
309
|
status: "completed",
|
|
310
310
|
output: truncated ? output.slice(0, PER_TASK_OUTPUT_CAP) : output,
|
|
311
311
|
// `undefined` rather than `false`: the key is absent on an untruncated run, so it serializes
|
|
@@ -321,6 +321,24 @@ export class SubagentRunRecorder {
|
|
|
321
321
|
failure: undefined,
|
|
322
322
|
finishedAt: new Date().toISOString(),
|
|
323
323
|
});
|
|
324
|
+
// THE CHILD'S MONEY IS THE CALLER'S MONEY, AND IT IS ADDED ONCE.
|
|
325
|
+
//
|
|
326
|
+
// The run has just been settled with its FINAL accumulated usage, and that is the number the
|
|
327
|
+
// dispatching conversation's ledger is given - not one message's worth, and not one addition
|
|
328
|
+
// per message the child produced. A project budget reads the conversation's ledger, so a child
|
|
329
|
+
// that is not in there is a child nobody is charged for.
|
|
330
|
+
//
|
|
331
|
+
// A FAILURE HERE IS ANNOUNCED AND SWALLOWED: the run record is already on disk, so the work is
|
|
332
|
+
// real and recorded, and losing the ledger entry must not turn a finished run into a failed one.
|
|
333
|
+
// The next start folds it in - the startup sweep accounts for every run whose usage has not been
|
|
334
|
+
// accounted for - so the money is late rather than lost.
|
|
335
|
+
try {
|
|
336
|
+
await this.hooks.store.accountSubagentUsage(id);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
console.warn(`Super Pi: the usage of sub-agent run ${id} could not be added to the usage of conversation ${conversationId}: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
340
|
+
}
|
|
341
|
+
return settled;
|
|
324
342
|
}
|
|
325
343
|
catch (error) {
|
|
326
344
|
const stopped = error instanceof SubagentCancelled;
|
|
@@ -262,7 +262,7 @@ export declare const SESSION_STOP_TOOL_NAME = "session_stop";
|
|
|
262
262
|
export declare const SESSION_REPORT_TOOL_NAME = "session_report";
|
|
263
263
|
export declare const SESSION_STOP_SUBTREE_TOOL_NAME = "session_stop_subtree";
|
|
264
264
|
/** Model-facing description for session_spawn. */
|
|
265
|
-
export declare const SESSION_SPAWN_DESCRIPTION = "Create a
|
|
265
|
+
export declare const SESSION_SPAWN_DESCRIPTION = "Create a child conversation in the current workspace and immediately dispatch its initial task. You can specify a primary agent as the child's role.\n\nDIFFERENCE FROM THE 'task' TOOL:\n- The 'task' tool delegates to a transient, one-shot subagent process (scout, critic, patcher) that runs once, returns its final text, and terminates. It has no conversation history and does not appear in the workspace sidebar.\n- 'session_spawn' creates a real, persistent child CONVERSATION in Super Pi. It appears in the sidebar, retains multi-turn message history, can receive follow-up messages via session_dispatch, can be inspected via session_status, and can be stopped via session_stop. The user can also switch to it in the Web UI.\n\nWHEN TO USE THIS:\n- Long-running, multi-phase tracks of work that require ongoing context, multiple turns of dialogue, or independent user visibility.\n- Multi-phase or complex work where the user needs each step to be visible and available for inspection or intervention: create a child session with session_spawn.\n- Parallel work streams (e.g., Track A: backend refactor, Track B: frontend component update) where both need full conversational capabilities.\n- When the user asks you to act as an orchestrator/manager who delegates to multiple sessions.\n\nWHEN NOT TO USE THIS:\n- For one-off, focused delegation with a single answer: use 'task' (with scout/critic/patcher). It is faster, cheaper, and returns directly to your transcript.\n- For running shell commands or build watchers: use 'run_async'.\n- For a single one-off delegation with a single answer: use 'task'.\n\nUsage notes:\n1. Spawning is ASYNCHRONOUS by default: it creates the conversation, enqueues the initial task, and returns the new conversationId immediately. The sibling session runs in the background.\n2. The initial task should provide clear context and goals since the new session starts with a fresh conversation history.\n3. Use session_status with the returned conversationId to check progress, or session_dispatch to send further messages.\n4. The new session reports completion to its DIRECT parent automatically. 'parentConversationId' optionally selects that parent; by default it is the calling session.\n5. The new session always belongs to the calling session's workspace. Pass 'cwd' as a workspace-relative subdirectory (for example, 'packages/a') when it should work below the workspace root; absolute paths and traversal are rejected.\n6. Optionally pass 'model' to choose which model the NEW session runs on: {\"provider\":\"...\",\"modelId\":\"...\"} - the same \"provider/modelId\" pair the model picker shows, and BOTH halves are required. Leave 'model' out and the new session starts on the workspace default model.\n7. Optionally pass 'watchdogTimeoutSeconds' to declare how long YOU are willing to wait for a report before you want to be reminded: a positive number of seconds. If the new session stays silent that long, you receive a '[Watchdog \u63D0\u9192]' message naming it. A report from the child (session_report) restarts the clock; the watchdog never stops the child. Leave it out to run without a watchdog.";
|
|
266
266
|
/** Model-facing description for session_status. */
|
|
267
267
|
export declare const SESSION_STATUS_DESCRIPTION = "Inspect the progress, run state, and latest activity of sibling conversations created by the orchestrator.\n\nWHEN TO USE THIS:\n- To check whether a spawned sibling conversation is still running, has finished (idle), is stopped, or encountered an error.\n- To read the latest assistant progress snippet without bloating your context with the session's entire raw message transcript.\n- To list all sibling conversations under your coordination when conversationId is omitted.\n\nUsage notes:\n1. When 'conversationId' is provided: returns a compact summary for that specific session, including its run status ('idle', 'running', 'waiting', 'stopped', 'failed'), messageCount, turnCount, lastAssistantSnippet, lastUserSnippet, activeTool, and error (if any).\n2. When 'conversationId' is omitted: returns a summary list of sibling conversations whose direct parent is 'parentConversationId' (the calling session by default).\n3. By default it never returns full transcripts; concise summaries keep your context window lean. Set 'includeOutput' with a conversationId to read the final output from its result file, capped to the last 64Ki characters with an explicit truncation notice.";
|
|
268
268
|
/** Model-facing description for session_dispatch. */
|
package/dist/server/task-tool.js
CHANGED
|
@@ -367,21 +367,22 @@ export const SESSION_STOP_TOOL_NAME = "session_stop";
|
|
|
367
367
|
export const SESSION_REPORT_TOOL_NAME = "session_report";
|
|
368
368
|
export const SESSION_STOP_SUBTREE_TOOL_NAME = "session_stop_subtree";
|
|
369
369
|
/** Model-facing description for session_spawn. */
|
|
370
|
-
export const SESSION_SPAWN_DESCRIPTION = `Create a
|
|
370
|
+
export const SESSION_SPAWN_DESCRIPTION = `Create a child conversation in the current workspace and immediately dispatch its initial task. You can specify a primary agent as the child's role.
|
|
371
371
|
|
|
372
372
|
DIFFERENCE FROM THE 'task' TOOL:
|
|
373
373
|
- The 'task' tool delegates to a transient, one-shot subagent process (scout, critic, patcher) that runs once, returns its final text, and terminates. It has no conversation history and does not appear in the workspace sidebar.
|
|
374
|
-
- 'session_spawn' creates a real, persistent
|
|
374
|
+
- 'session_spawn' creates a real, persistent child CONVERSATION in Super Pi. It appears in the sidebar, retains multi-turn message history, can receive follow-up messages via session_dispatch, can be inspected via session_status, and can be stopped via session_stop. The user can also switch to it in the Web UI.
|
|
375
375
|
|
|
376
376
|
WHEN TO USE THIS:
|
|
377
377
|
- Long-running, multi-phase tracks of work that require ongoing context, multiple turns of dialogue, or independent user visibility.
|
|
378
|
+
- Multi-phase or complex work where the user needs each step to be visible and available for inspection or intervention: create a child session with session_spawn.
|
|
378
379
|
- Parallel work streams (e.g., Track A: backend refactor, Track B: frontend component update) where both need full conversational capabilities.
|
|
379
380
|
- When the user asks you to act as an orchestrator/manager who delegates to multiple sessions.
|
|
380
381
|
|
|
381
382
|
WHEN NOT TO USE THIS:
|
|
382
383
|
- For one-off, focused delegation with a single answer: use 'task' (with scout/critic/patcher). It is faster, cheaper, and returns directly to your transcript.
|
|
383
384
|
- For running shell commands or build watchers: use 'run_async'.
|
|
384
|
-
- For a
|
|
385
|
+
- For a single one-off delegation with a single answer: use 'task'.
|
|
385
386
|
|
|
386
387
|
Usage notes:
|
|
387
388
|
1. Spawning is ASYNCHRONOUS by default: it creates the conversation, enqueues the initial task, and returns the new conversationId immediately. The sibling session runs in the background.
|
package/dist/server/types.d.ts
CHANGED
|
@@ -464,7 +464,17 @@ export type Conversation = {
|
|
|
464
464
|
status: RunStatus;
|
|
465
465
|
messages: Message[];
|
|
466
466
|
archivedAt?: string;
|
|
467
|
-
/**
|
|
467
|
+
/**
|
|
468
|
+
* Cumulative usage observed for this conversation: its OWN assistant messages AND the one-shot
|
|
469
|
+
* (sub-agent) calls it dispatched.
|
|
470
|
+
*
|
|
471
|
+
* THE SUB-AGENT'S USAGE IS IN HERE ON PURPOSE. A child Pi spends real money, and a budget that
|
|
472
|
+
* only counts the parent's own turns is a budget that can be multiplied past its own cap by
|
|
473
|
+
* dispatching children instead of answering - which is exactly what design §5 forbids, because
|
|
474
|
+
* "写预算的人必须能自己算出这个数是多少钱" is only true if the number includes every call the
|
|
475
|
+
* conversation made. The child's final accumulated usage is folded in ONCE, when the run settles
|
|
476
|
+
* (see `Store.accountSubagentUsage`), so the sum here equals what was really spent.
|
|
477
|
+
*/
|
|
468
478
|
usage?: SubagentUsage;
|
|
469
479
|
/** False means cost is only a sum of reported costs, not a complete price (zero is not "free"). */
|
|
470
480
|
usageCostKnown?: boolean;
|
|
@@ -601,6 +611,21 @@ export type AgentDefinition = {
|
|
|
601
611
|
prompt: string;
|
|
602
612
|
/** The directory this agent definition was discovered from. */
|
|
603
613
|
sourceDir?: string;
|
|
614
|
+
/**
|
|
615
|
+
* Whether this definition came from the PACKAGE'S OWN agents directory - the ones Super Pi ships -
|
|
616
|
+
* rather than from a directory the user pointed the app at.
|
|
617
|
+
*
|
|
618
|
+
* WHY IT IS DECIDED BY THE SERVER AND NOT BY THE CLIENT. The picker used to interleave the two
|
|
619
|
+
* kinds alphabetically, so the built-in agents - the ones with a colour dot - were scattered among
|
|
620
|
+
* the user's own. The client cannot work that out on its own: it is told where each definition came
|
|
621
|
+
* from, but "is this directory the package's own" is a question about the app's installation, and
|
|
622
|
+
* only the server knows where it is installed. So the server answers it here, at read time, and the
|
|
623
|
+
* client just orders by the answer.
|
|
624
|
+
*
|
|
625
|
+
* It describes the definition that WON, not the id: when a user's directory shadows a built-in
|
|
626
|
+
* agent of the same id, the user's file is the one that is listed, and it is the user's.
|
|
627
|
+
*/
|
|
628
|
+
builtin?: boolean;
|
|
604
629
|
};
|
|
605
630
|
/**
|
|
606
631
|
* What the picker receives. The prompt body is deliberately NOT sent: it is a whole system prompt,
|
|
@@ -830,6 +855,20 @@ export type SubagentRun = {
|
|
|
830
855
|
/** True when `output` was cut for the model's benefit; the browser still saw all of it. */
|
|
831
856
|
outputTruncated?: boolean;
|
|
832
857
|
usage?: SubagentUsage;
|
|
858
|
+
/**
|
|
859
|
+
* TRUE once this run's usage has been folded into the usage ledger of the conversation that
|
|
860
|
+
* dispatched it.
|
|
861
|
+
*
|
|
862
|
+
* IT IS THE FLAG THAT MAKES THE FOLD HAPPEN EXACTLY ONCE. The child's money is added to the
|
|
863
|
+
* parent conversation's `usage` when the run settles, and a project budget reads that ledger -
|
|
864
|
+
* so a run whose usage were folded twice would be billed twice, and a run whose usage were never
|
|
865
|
+
* folded would be free. The flag says which side of that line this run is on, and it is written
|
|
866
|
+
* in the SAME store write as the ledger entry, so there is no window in which the two disagree.
|
|
867
|
+
*
|
|
868
|
+
* ABSENT means "not accounted for yet", which is what a `state.json` written before this feature
|
|
869
|
+
* existed says about every run in it - and what the startup sweep repairs, once, on the next boot.
|
|
870
|
+
*/
|
|
871
|
+
usageAccounted?: boolean;
|
|
833
872
|
error?: string;
|
|
834
873
|
/**
|
|
835
874
|
* How long the run took, measured by THIS server between writing the `running` row and settling it.
|
|
@@ -949,6 +988,7 @@ export type ProjectTask = {
|
|
|
949
988
|
priority: "low" | "medium" | "high" | "urgent";
|
|
950
989
|
dueAt?: string;
|
|
951
990
|
blockedReason?: string;
|
|
991
|
+
blockedReportedByConversationId?: string;
|
|
952
992
|
output?: string;
|
|
953
993
|
evidenceRefs?: string[];
|
|
954
994
|
/** The acceptance of this task. `lightweight: true` means the executor accepted its own
|
|
@@ -989,6 +1029,9 @@ export type ProjectEscalation = {
|
|
|
989
1029
|
decision?: string;
|
|
990
1030
|
createdAt: string;
|
|
991
1031
|
resolvedAt?: string;
|
|
1032
|
+
audience?: "supervisor" | "boss";
|
|
1033
|
+
targetConversationId?: string;
|
|
1034
|
+
resolvedByConversationId?: string;
|
|
992
1035
|
};
|
|
993
1036
|
export type ProjectEvent = {
|
|
994
1037
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evandu/super-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.166",
|
|
4
4
|
"description": "Web control plane and companion for Pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"scripts/stream-transport.test.mjs",
|
|
49
49
|
"scripts/remote-transport.test.mjs",
|
|
50
50
|
"scripts/browser-safe-port.test.mjs",
|
|
51
|
+
"scripts/agent-picker-order.test.mjs",
|
|
51
52
|
"scripts/inline-thinking.test.mjs",
|
|
52
53
|
"scripts/inline-thinking-close-tag.test.mjs",
|
|
53
54
|
"scripts/pi-session.test.mjs",
|
|
@@ -69,6 +70,7 @@
|
|
|
69
70
|
"scripts/verify-source-text.test.mjs",
|
|
70
71
|
"scripts/runtime-extension-ui.test.mjs",
|
|
71
72
|
"scripts/agent-mechanism.test.mjs",
|
|
73
|
+
"scripts/builtin-agents-project.test.mjs",
|
|
72
74
|
"scripts/super-pi-tool-names.test.mjs",
|
|
73
75
|
"scripts/agent-wiring.test.mjs",
|
|
74
76
|
"scripts/message-timestamps.test.mjs",
|
|
@@ -85,6 +87,7 @@
|
|
|
85
87
|
"scripts/session-orchestrator-tools.test.mjs",
|
|
86
88
|
"scripts/project-service.test.mjs",
|
|
87
89
|
"scripts/project-api-scope.test.mjs",
|
|
90
|
+
"scripts/project-report-chain.test.mjs",
|
|
88
91
|
"scripts/session-tree-collapse.test.mjs",
|
|
89
92
|
"scripts/archive-cascade.test.mjs",
|
|
90
93
|
"scripts/workspace-cwd.test.mjs",
|
|
@@ -94,6 +97,7 @@
|
|
|
94
97
|
"scripts/subagent-noise.test.mjs",
|
|
95
98
|
"scripts/subagent-response-receipt.test.mjs",
|
|
96
99
|
"scripts/subagent-runs.test.mjs",
|
|
100
|
+
"scripts/subagent-usage-accounting.test.mjs",
|
|
97
101
|
"scripts/ws-flood-regression.test.mjs",
|
|
98
102
|
"scripts/conversation-reconcile.test.mjs",
|
|
99
103
|
"scripts/subagent-dispatch.test.mjs",
|
|
@@ -128,6 +132,7 @@
|
|
|
128
132
|
"scripts/git-write-endpoint-guard.test.mjs",
|
|
129
133
|
"scripts/question-stop.test.mjs",
|
|
130
134
|
"scripts/background-result-body.test.mjs",
|
|
135
|
+
"scripts/browser-click.test.mjs",
|
|
131
136
|
"scripts/question-request-size.test.mjs",
|
|
132
137
|
"scripts/md-render.test.mjs",
|
|
133
138
|
"scripts/revert-session-format.test.mjs",
|
|
@@ -224,7 +229,9 @@
|
|
|
224
229
|
"scripts/session-report-tool.test.mjs",
|
|
225
230
|
"scripts/session-stop-subtree.test.mjs",
|
|
226
231
|
"scripts/awaiting-background-status.test.mjs",
|
|
227
|
-
"scripts/project-lifecycle-e2e.test.mjs"
|
|
232
|
+
"scripts/project-lifecycle-e2e.test.mjs",
|
|
233
|
+
"scripts/agent-tiers.test.mjs",
|
|
234
|
+
"scripts/design-principles.test.mjs"
|
|
228
235
|
],
|
|
229
236
|
"check:browser": [
|
|
230
237
|
"scripts/interrupt-browser.test.mjs",
|
|
@@ -258,6 +265,8 @@
|
|
|
258
265
|
"scripts/workspace-identity-browser.test.mjs",
|
|
259
266
|
"scripts/sidebar-tools-browser.test.mjs",
|
|
260
267
|
"scripts/project-employee-panel-browser.test.mjs",
|
|
268
|
+
"scripts/project-evidence-panel-browser.test.mjs",
|
|
269
|
+
"scripts/builtin-agents-project-browser.test.mjs",
|
|
261
270
|
"scripts/tool-runtime-browser.test.mjs",
|
|
262
271
|
"scripts/session-context-menu-browser.test.mjs",
|
|
263
272
|
"scripts/archive-cascade-browser.test.mjs",
|