@evandu/super-pi 0.1.132 → 0.1.133
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/dist/.super-pi-check-inputs.json +4 -0
- package/dist/client/.super-pi-client-sources.json +5 -5
- package/dist/client/assets/{CodeMirrorEditor-ktyL2PcX.js → CodeMirrorEditor-CZZfGD0U.js} +2 -2
- package/dist/client/assets/{dist-8pdzh6h1.js → dist-0FH-oFco.js} +1 -1
- package/dist/client/assets/{dist-Bg9zLfex.js → dist-4Pts9xPO.js} +1 -1
- package/dist/client/assets/{dist-Hj_dWgPr.js → dist-B5ruVtUG.js} +1 -1
- package/dist/client/assets/{dist-Cu75NyQ0.js → dist-BJinarhP.js} +1 -1
- package/dist/client/assets/{dist-CMscy2Nn.js → dist-BffE00vI.js} +1 -1
- package/dist/client/assets/{dist-CoszOk5k.js → dist-BtTFCNVQ.js} +1 -1
- package/dist/client/assets/{dist-DvHghuGd.js → dist-C2nuBCP4.js} +1 -1
- package/dist/client/assets/{dist-BAJCrbnX.js → dist-CApAttfj.js} +1 -1
- package/dist/client/assets/{dist-CPLCQDqy.js → dist-CE5BnwJa.js} +1 -1
- package/dist/client/assets/{dist-BRjGckmQ.js → dist-CFPXj4ws.js} +1 -1
- package/dist/client/assets/{dist-BjZUYxLd.js → dist-CUKaDTyO.js} +1 -1
- package/dist/client/assets/{dist-vsMBLoxV.js → dist-CYD2SqmG.js} +1 -1
- package/dist/client/assets/{dist-B8OZ-n_C.js → dist-CxsunljU.js} +1 -1
- package/dist/client/assets/{dist-D_Zdnhm8.js → dist-D4x1XTQb.js} +1 -1
- package/dist/client/assets/{dist-Zhdkfvng.js → dist-DHqqpxo4.js} +1 -1
- package/dist/client/assets/{dist-DNZMXYNq.js → dist-DOYWHMIq.js} +1 -1
- package/dist/client/assets/{dist-BJi5HmH4.js → dist-DQf5nNxa.js} +1 -1
- package/dist/client/assets/{dist-CgmISAMr.js → dist-DbbBgSwL.js} +1 -1
- package/dist/client/assets/{dist-Bv6P2Rcp.js → dist-LFfokYJs.js} +1 -1
- package/dist/client/assets/{dist-C6iAHGvb.js → dist-aL2NGji_.js} +1 -1
- package/dist/client/assets/{dist--igDAzRS.js → dist-azDN8a65.js} +1 -1
- package/dist/client/assets/{dist-60usx__o.js → dist-fYp8Xwrx.js} +1 -1
- package/dist/client/assets/index-YEN4ysHF.js +100 -0
- package/dist/client/index.html +1 -1
- package/dist/server/agents.js +21 -1
- package/dist/server/application/feishu-service.d.ts +57 -10
- package/dist/server/application/feishu-service.js +118 -24
- package/dist/server/application/run-service.js +15 -1
- package/dist/server/feishu-cli.d.ts +18 -2
- package/dist/server/feishu-cli.js +46 -12
- package/dist/server/feishu-config.d.ts +68 -0
- package/dist/server/feishu-config.js +84 -0
- package/dist/server/feishu.d.ts +187 -0
- package/dist/server/feishu.js +695 -0
- package/dist/server/http-server.js +20 -0
- package/dist/server/persistence/sqlite-state-repository.js +18 -0
- package/dist/server/pi-session-events.js +10 -2
- package/dist/server/pi-session-pool.d.ts +33 -1
- package/dist/server/pi-session-pool.js +20 -2
- package/dist/server/runtime.d.ts +26 -1
- package/dist/server/runtime.js +52 -9
- package/package.json +10 -1
- package/dist/client/assets/index-ChyemF2I.js +0 -100
|
@@ -913,6 +913,26 @@ export function createHttpServer(options) {
|
|
|
913
913
|
}
|
|
914
914
|
return;
|
|
915
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* CHECKS THE CONNECTION, and says why it does not work when it does not.
|
|
918
|
+
*
|
|
919
|
+
* The status already verifies the credential against Feishu, so this is the same answer shaped
|
|
920
|
+
* for a button - the difference is that a failure comes back as a MESSAGE AND A HINT rather
|
|
921
|
+
* than as a state the panel has to interpret.
|
|
922
|
+
*/
|
|
923
|
+
if (url.pathname === "/api/feishu/verify" && request.method === "POST") {
|
|
924
|
+
if (!context.feishu) {
|
|
925
|
+
response.writeHead(503).end();
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (!isLocalOrPrivateAddress(remote)) {
|
|
929
|
+
json(403, { error: "测试飞书连接只允许来自本机或局域网的请求。" });
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const result = await context.feishu.verify();
|
|
933
|
+
json(result.ok ? 200 : 400, result);
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
916
936
|
if (url.pathname === "/api/shutdown") {
|
|
917
937
|
if (request.method !== "POST" || !isLocalOrPrivateAddress(remote)) {
|
|
918
938
|
response.writeHead(403, { "content-type": "text/plain; charset=utf-8" }).end("Shutdown requires a loopback or private network POST");
|
|
@@ -120,6 +120,24 @@ export class SqliteStateRepository {
|
|
|
120
120
|
}
|
|
121
121
|
else if (transaction.kind === "collections.remove") {
|
|
122
122
|
this.db.prepare("DELETE FROM collections WHERE name=?").run(transaction.name);
|
|
123
|
+
// AND THE BASE SNAPSHOT MUST STOP CARRYING IT TOO. Deleting only the overlay row was not
|
|
124
|
+
// enough: this loader treats an overlay as a PURE INCREMENT - "no row for this name" means
|
|
125
|
+
// "read it from the base snapshot" - so the base's stale array came back on the next load,
|
|
126
|
+
// and a scheduled task the user had deleted was resurrected and ran again on its old
|
|
127
|
+
// schedule (ledger TEST-038). "This collection is empty" is stored as the ABSENCE of the key
|
|
128
|
+
// (app-state-repository.ts), so the base has to lose the key as well - the row's deletion
|
|
129
|
+
// alone cannot express it, because the base is where the value would come from.
|
|
130
|
+
//
|
|
131
|
+
// The json backend has no incremental path and falls back to a whole-state save, which is
|
|
132
|
+
// why it was always right; that asymmetry is what makes this a defect and not a choice.
|
|
133
|
+
const base = this.db.prepare("SELECT json FROM state WHERE id=1").get();
|
|
134
|
+
if (base) {
|
|
135
|
+
const parsed = JSON.parse(String(base.json));
|
|
136
|
+
if (transaction.name in parsed) {
|
|
137
|
+
delete parsed[transaction.name];
|
|
138
|
+
this.db.prepare("UPDATE state SET json=? WHERE id=1").run(JSON.stringify(parsed));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
123
141
|
}
|
|
124
142
|
else if (transaction.kind === "conversation.messages") {
|
|
125
143
|
// A DIFF, NOT A REWRITE. This used to DELETE the conversation's rows and re-INSERT every
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* be announced as unhandled is still announced as unhandled - this bridge changes WHERE the events come
|
|
17
17
|
* from, not what they mean.
|
|
18
18
|
*/
|
|
19
|
-
import { describeToolCall, describeToolCallArgs, extractPartialResultText } from "./tool-calls.js";
|
|
19
|
+
import { describeToolCall, describeToolCallArgs, extractPartialResultText, readQuestionCall } from "./tool-calls.js";
|
|
20
20
|
import { toolTargetPath } from "./file-changes.js";
|
|
21
21
|
/** Keeps only real strings, so a malformed queue snapshot cannot put objects into a broadcast. */
|
|
22
22
|
const textList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : undefined;
|
|
@@ -254,7 +254,15 @@ export function handleSessionEvent(event, queue, handlers, report, failure) {
|
|
|
254
254
|
return true;
|
|
255
255
|
const name = record.toolName ?? "unknown";
|
|
256
256
|
try {
|
|
257
|
-
|
|
257
|
+
// A `question` CALL IS READ HERE, AT `tool_execution_start` - the same place the one-shot path
|
|
258
|
+
// reads it, and for the same reason: the card on screen must be the tool call the model is
|
|
259
|
+
// actually blocked in, with the same identity and the same lifecycle. This path left the
|
|
260
|
+
// question out, so the host never saw a pending question and the model was told the session
|
|
261
|
+
// had no question waiting for an answer - the `question` tool was simply dead on the SDK
|
|
262
|
+
// session path, which is the PRODUCTION default (ledger TEST-037). Two paths must give the
|
|
263
|
+
// same answer to "what is a question call".
|
|
264
|
+
const question = readQuestionCall(name, record.args);
|
|
265
|
+
handlers.onToolCall?.({ id, name, detail: describeToolCall(name, record.args), args: describeToolCallArgs(record.args), ...(question ? { question } : {}) });
|
|
258
266
|
}
|
|
259
267
|
catch (error) {
|
|
260
268
|
console.warn(`PiRpcRuntime: the tool call handler threw: ${error instanceof Error ? error.message : "unknown error"}`);
|
|
@@ -87,6 +87,22 @@ export declare class PiSessionPool {
|
|
|
87
87
|
* where that gets fixed.
|
|
88
88
|
*/
|
|
89
89
|
private readonly modelEpoch;
|
|
90
|
+
/**
|
|
91
|
+
* Mints the OTHER per-session credentials the extensions need, at the moment the session is born.
|
|
92
|
+
*
|
|
93
|
+
* WHY A SECOND CALLBACK AND NOT PART OF THE CAPABILITY. The memory and knowledge extensions each
|
|
94
|
+
* need their own credential - the memory one even carries per-caller flags (read-only for a
|
|
95
|
+
* sub-agent) that a single session capability cannot express. The one-shot path has always minted
|
|
96
|
+
* them per turn; the SESSION path minted none, so those two extensions returned before registering
|
|
97
|
+
* a single tool or injecting a single line of prompt, and both features were absent in production -
|
|
98
|
+
* the SDK session path is the default backend, and only the test scripts use the one-shot path
|
|
99
|
+
* (ledger TEST-039).
|
|
100
|
+
*
|
|
101
|
+
* They are minted HERE, where the session is born, for the same reason the capability is: the
|
|
102
|
+
* extension reads its environment ONCE, when it is loaded, so a token minted per turn would be
|
|
103
|
+
* replaced before the second turn and the extension would still be holding the first turn's value.
|
|
104
|
+
*/
|
|
105
|
+
private readonly mintExtensionTokens;
|
|
90
106
|
private readonly sessions;
|
|
91
107
|
constructor(
|
|
92
108
|
/** Where Pi keeps its session files. */
|
|
@@ -122,7 +138,23 @@ export declare class PiSessionPool {
|
|
|
122
138
|
* settings window makes every live session unable to reach the new model, and the next turn is
|
|
123
139
|
* where that gets fixed.
|
|
124
140
|
*/
|
|
125
|
-
modelEpoch?: () => number
|
|
141
|
+
modelEpoch?: () => number,
|
|
142
|
+
/**
|
|
143
|
+
* Mints the OTHER per-session credentials the extensions need, at the moment the session is born.
|
|
144
|
+
*
|
|
145
|
+
* WHY A SECOND CALLBACK AND NOT PART OF THE CAPABILITY. The memory and knowledge extensions each
|
|
146
|
+
* need their own credential - the memory one even carries per-caller flags (read-only for a
|
|
147
|
+
* sub-agent) that a single session capability cannot express. The one-shot path has always minted
|
|
148
|
+
* them per turn; the SESSION path minted none, so those two extensions returned before registering
|
|
149
|
+
* a single tool or injecting a single line of prompt, and both features were absent in production -
|
|
150
|
+
* the SDK session path is the default backend, and only the test scripts use the one-shot path
|
|
151
|
+
* (ledger TEST-039).
|
|
152
|
+
*
|
|
153
|
+
* They are minted HERE, where the session is born, for the same reason the capability is: the
|
|
154
|
+
* extension reads its environment ONCE, when it is loaded, so a token minted per turn would be
|
|
155
|
+
* replaced before the second turn and the extension would still be holding the first turn's value.
|
|
156
|
+
*/
|
|
157
|
+
mintExtensionTokens?: (conversationId: string) => Promise<Record<string, string>> | Record<string, string>);
|
|
126
158
|
/**
|
|
127
159
|
* The session for a conversation, built if there is none and REPLACED if the live one is stale.
|
|
128
160
|
*
|
|
@@ -49,6 +49,7 @@ export class PiSessionPool {
|
|
|
49
49
|
factory;
|
|
50
50
|
mintCapability;
|
|
51
51
|
modelEpoch;
|
|
52
|
+
mintExtensionTokens;
|
|
52
53
|
sessions = new Map();
|
|
53
54
|
constructor(
|
|
54
55
|
/** Where Pi keeps its session files. */
|
|
@@ -84,7 +85,23 @@ export class PiSessionPool {
|
|
|
84
85
|
* settings window makes every live session unable to reach the new model, and the next turn is
|
|
85
86
|
* where that gets fixed.
|
|
86
87
|
*/
|
|
87
|
-
modelEpoch = () => 0
|
|
88
|
+
modelEpoch = () => 0,
|
|
89
|
+
/**
|
|
90
|
+
* Mints the OTHER per-session credentials the extensions need, at the moment the session is born.
|
|
91
|
+
*
|
|
92
|
+
* WHY A SECOND CALLBACK AND NOT PART OF THE CAPABILITY. The memory and knowledge extensions each
|
|
93
|
+
* need their own credential - the memory one even carries per-caller flags (read-only for a
|
|
94
|
+
* sub-agent) that a single session capability cannot express. The one-shot path has always minted
|
|
95
|
+
* them per turn; the SESSION path minted none, so those two extensions returned before registering
|
|
96
|
+
* a single tool or injecting a single line of prompt, and both features were absent in production -
|
|
97
|
+
* the SDK session path is the default backend, and only the test scripts use the one-shot path
|
|
98
|
+
* (ledger TEST-039).
|
|
99
|
+
*
|
|
100
|
+
* They are minted HERE, where the session is born, for the same reason the capability is: the
|
|
101
|
+
* extension reads its environment ONCE, when it is loaded, so a token minted per turn would be
|
|
102
|
+
* replaced before the second turn and the extension would still be holding the first turn's value.
|
|
103
|
+
*/
|
|
104
|
+
mintExtensionTokens = () => ({})) {
|
|
88
105
|
this.sessionDir = sessionDir;
|
|
89
106
|
this.repositoryRoot = repositoryRoot;
|
|
90
107
|
this.agentDir = agentDir;
|
|
@@ -92,6 +109,7 @@ export class PiSessionPool {
|
|
|
92
109
|
this.factory = factory;
|
|
93
110
|
this.mintCapability = mintCapability;
|
|
94
111
|
this.modelEpoch = modelEpoch;
|
|
112
|
+
this.mintExtensionTokens = mintExtensionTokens;
|
|
95
113
|
}
|
|
96
114
|
/**
|
|
97
115
|
* The session for a conversation, built if there is none and REPLACED if the live one is stale.
|
|
@@ -138,7 +156,7 @@ export class PiSessionPool {
|
|
|
138
156
|
// server separately requires that a turn is RUNNING for the token to be usable, which is what keeps a
|
|
139
157
|
// longer-lived token from being a weaker credential than a per-turn one.
|
|
140
158
|
const capability = this.mintCapability(request.conversationId);
|
|
141
|
-
const extensionEnv = { ...request.extensionEnv, [CAPABILITY_ENV]: capability, [TOOLS_CAPABILITY_ENV]: capability };
|
|
159
|
+
const extensionEnv = { ...request.extensionEnv, [CAPABILITY_ENV]: capability, [TOOLS_CAPABILITY_ENV]: capability, ...await this.mintExtensionTokens(request.conversationId) };
|
|
142
160
|
const building = this.build({ ...request, extensionEnv }, fingerprint);
|
|
143
161
|
if (process.env.SUPER_PI_TRACE_LATENCY === "1")
|
|
144
162
|
console.warn(`[super-pi latency] conversation=${request.conversationId} phase=session.build.begin`);
|
package/dist/server/runtime.d.ts
CHANGED
|
@@ -164,7 +164,11 @@ export interface AnswerInput {
|
|
|
164
164
|
onToolPreparing?(call: ToolCallPreparing): void;
|
|
165
165
|
/** Called while the primary model is emitting reasoning, so the UI can show live progress. A call means
|
|
166
166
|
* the model is STILL THINKING - there is no separate flag, because Pi does not send one. */
|
|
167
|
-
|
|
167
|
+
/** Called while the primary model is emitting reasoning, so the UI can show live progress. A call means
|
|
168
|
+
* the model is STILL THINKING - there is no separate flag, because Pi does not send one.
|
|
169
|
+
* The handler may be async: the reasoning signal is the same "the model is producing" signal as
|
|
170
|
+
* a text delta, and the caller waits for it so the state it writes is ordered with the delta. */
|
|
171
|
+
onThinking?(delta: string): void | Promise<void>;
|
|
168
172
|
/** Called when the reasoning phase ends - the only signal that it has. */
|
|
169
173
|
onThinkingEnd?(): void;
|
|
170
174
|
}
|
|
@@ -560,6 +564,12 @@ export declare class PiRpcRuntime implements AgentRuntime {
|
|
|
560
564
|
private readonly options;
|
|
561
565
|
private memory?;
|
|
562
566
|
setMemory(memory: ActiveMemory): void;
|
|
567
|
+
/**
|
|
568
|
+
* The memory and knowledge credentials minted for each LIVE session, so they can be released when
|
|
569
|
+
* the session goes. A credential whose session is gone must not keep working - that is exactly the
|
|
570
|
+
* shape of a secret with no owner.
|
|
571
|
+
*/
|
|
572
|
+
private readonly sessionExtensionTokens;
|
|
563
573
|
private knowledge?;
|
|
564
574
|
setKnowledge(knowledge: KnowledgeBase): void;
|
|
565
575
|
private readonly runs;
|
|
@@ -841,6 +851,21 @@ export declare class PiRpcRuntime implements AgentRuntime {
|
|
|
841
851
|
* The TODO ENDPOINT is the one value here that is NOT per-turn derived from disk: it is this server's
|
|
842
852
|
* own address, so it is the same string on every turn and is read once from the constructor's options.
|
|
843
853
|
*/
|
|
854
|
+
/**
|
|
855
|
+
* THE MEMORY AND KNOWLEDGE CREDENTIALS FOR A NEW SESSION.
|
|
856
|
+
*
|
|
857
|
+
* Both extensions read their environment ONCE, when they are loaded, so these are minted where the
|
|
858
|
+
* session is born - the same place, and for the same reason, as the session's capability. The
|
|
859
|
+
* one-shot path has always minted them (per turn); the session path minted none, so the memory and
|
|
860
|
+
* knowledge extensions returned before registering a tool or injecting a prompt, and both features
|
|
861
|
+
* were missing in production - the SDK session path is the DEFAULT backend, and only the test
|
|
862
|
+
* scripts take the one-shot path (ledger TEST-039).
|
|
863
|
+
*
|
|
864
|
+
* The endpoints are published as global environment variables (index.ts), and they are repeated
|
|
865
|
+
* here so the extension's contract is stated in one place: an endpoint and a credential travel
|
|
866
|
+
* together, and the credential is what proves the caller.
|
|
867
|
+
*/
|
|
868
|
+
private mintSessionExtensionTokens;
|
|
844
869
|
/**
|
|
845
870
|
* The environment Super Pi's extensions read when they are loaded.
|
|
846
871
|
*
|
package/dist/server/runtime.js
CHANGED
|
@@ -289,6 +289,12 @@ export class PiRpcRuntime {
|
|
|
289
289
|
options;
|
|
290
290
|
memory;
|
|
291
291
|
setMemory(memory) { this.memory = memory; }
|
|
292
|
+
/**
|
|
293
|
+
* The memory and knowledge credentials minted for each LIVE session, so they can be released when
|
|
294
|
+
* the session goes. A credential whose session is gone must not keep working - that is exactly the
|
|
295
|
+
* shape of a secret with no owner.
|
|
296
|
+
*/
|
|
297
|
+
sessionExtensionTokens = new Map();
|
|
292
298
|
knowledge;
|
|
293
299
|
setKnowledge(knowledge) { this.knowledge = knowledge; }
|
|
294
300
|
runs = new Map();
|
|
@@ -466,7 +472,16 @@ export class PiRpcRuntime {
|
|
|
466
472
|
// ABOUT that session and stop being true with it.
|
|
467
473
|
this.sessionHosts.delete(conversationId);
|
|
468
474
|
this.reportedSessionFiles.delete(conversationId);
|
|
469
|
-
|
|
475
|
+
// The session held these credentials too, so they die with it.
|
|
476
|
+
const tokens = this.sessionExtensionTokens.get(conversationId);
|
|
477
|
+
if (tokens) {
|
|
478
|
+
if (tokens.memoryToken)
|
|
479
|
+
this.memory?.release(tokens.memoryToken);
|
|
480
|
+
if (tokens.knowledgeToken)
|
|
481
|
+
this.knowledge?.release(tokens.knowledgeToken);
|
|
482
|
+
this.sessionExtensionTokens.delete(conversationId);
|
|
483
|
+
}
|
|
484
|
+
}, options.sessionFactory ?? createPiSession, (conversationId) => mintCapability(conversationId), () => this.modelEpoch, (conversationId) => this.mintSessionExtensionTokens(conversationId));
|
|
470
485
|
}
|
|
471
486
|
/**
|
|
472
487
|
* The configuration of the session that was built for a conversation, for tests to inspect.
|
|
@@ -628,6 +643,37 @@ export class PiRpcRuntime {
|
|
|
628
643
|
* The TODO ENDPOINT is the one value here that is NOT per-turn derived from disk: it is this server's
|
|
629
644
|
* own address, so it is the same string on every turn and is read once from the constructor's options.
|
|
630
645
|
*/
|
|
646
|
+
/**
|
|
647
|
+
* THE MEMORY AND KNOWLEDGE CREDENTIALS FOR A NEW SESSION.
|
|
648
|
+
*
|
|
649
|
+
* Both extensions read their environment ONCE, when they are loaded, so these are minted where the
|
|
650
|
+
* session is born - the same place, and for the same reason, as the session's capability. The
|
|
651
|
+
* one-shot path has always minted them (per turn); the session path minted none, so the memory and
|
|
652
|
+
* knowledge extensions returned before registering a tool or injecting a prompt, and both features
|
|
653
|
+
* were missing in production - the SDK session path is the DEFAULT backend, and only the test
|
|
654
|
+
* scripts take the one-shot path (ledger TEST-039).
|
|
655
|
+
*
|
|
656
|
+
* The endpoints are published as global environment variables (index.ts), and they are repeated
|
|
657
|
+
* here so the extension's contract is stated in one place: an endpoint and a credential travel
|
|
658
|
+
* together, and the credential is what proves the caller.
|
|
659
|
+
*/
|
|
660
|
+
async mintSessionExtensionTokens(conversationId) {
|
|
661
|
+
const minted = {};
|
|
662
|
+
const env = {};
|
|
663
|
+
if (this.memory && (await this.memory.configuration()).enabled) {
|
|
664
|
+
minted.memoryToken = this.memory.issue(conversationId);
|
|
665
|
+
env.SUPER_PI_MEMORY_TOKEN = minted.memoryToken;
|
|
666
|
+
env.SUPER_PI_MEMORY_ENDPOINT = process.env.SUPER_PI_MEMORY_ENDPOINT ?? "";
|
|
667
|
+
}
|
|
668
|
+
if (this.knowledge && this.knowledge.enabled()) {
|
|
669
|
+
minted.knowledgeToken = this.knowledge.issue(conversationId);
|
|
670
|
+
env.SUPER_PI_KNOWLEDGE_TOKEN = minted.knowledgeToken;
|
|
671
|
+
env.SUPER_PI_KNOWLEDGE_ENDPOINT = process.env.SUPER_PI_KNOWLEDGE_ENDPOINT ?? "";
|
|
672
|
+
}
|
|
673
|
+
if (minted.memoryToken || minted.knowledgeToken)
|
|
674
|
+
this.sessionExtensionTokens.set(conversationId, minted);
|
|
675
|
+
return env;
|
|
676
|
+
}
|
|
631
677
|
/**
|
|
632
678
|
* The environment Super Pi's extensions read when they are loaded.
|
|
633
679
|
*
|
|
@@ -906,13 +952,10 @@ export class PiRpcRuntime {
|
|
|
906
952
|
// pushes the terminal item when Pi settles, and the consumer below reads it. This promise
|
|
907
953
|
// waits for that signal, and the race with the prompt's own failure is what keeps a turn that
|
|
908
954
|
// never settles from hanging forever - a hung turn is a conversation that never answers.
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
// never produce anything and the next message can never be sent.
|
|
914
|
-
new Promise((resolvePromise) => setTimeout(resolvePromise, 5_000)),
|
|
915
|
-
]);
|
|
955
|
+
// `prompt()` only means Pi accepted the request. Do not end this generator on a wall-clock
|
|
956
|
+
// timeout: the session may still be streaming or executing a tool, and ending here drops
|
|
957
|
+
// subsequent events while falsely allowing the next prompt onto a busy session.
|
|
958
|
+
await queue.settledSignal;
|
|
916
959
|
traceLatency(conversation.id, "session.settled", latencyStartedAt);
|
|
917
960
|
// THE REVERT BOUNDARY, recorded at the same moment the one-shot path records it: the turn has
|
|
918
961
|
// settled, so every entry this turn wrote - the user message included - is in the transcript.
|
|
@@ -1144,7 +1187,7 @@ export class PiRpcRuntime {
|
|
|
1144
1187
|
if (segment.kind === "text")
|
|
1145
1188
|
yield segment.text;
|
|
1146
1189
|
else if (segment.kind === "thinking")
|
|
1147
|
-
input.onThinking?.(segment.text);
|
|
1190
|
+
await input.onThinking?.(segment.text);
|
|
1148
1191
|
else
|
|
1149
1192
|
input.onThinkingEnd?.();
|
|
1150
1193
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evandu/super-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.133",
|
|
4
4
|
"description": "Web control plane and companion for Pi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -88,6 +88,10 @@
|
|
|
88
88
|
"scripts/followup.test.mjs",
|
|
89
89
|
"scripts/pi-followup-queue.test.mjs",
|
|
90
90
|
"scripts/background-result-delivery.test.mjs",
|
|
91
|
+
"scripts/thinking-status-promotion.test.mjs",
|
|
92
|
+
"scripts/question-call-session-path.test.mjs",
|
|
93
|
+
"scripts/withdrawn-message-persistence.test.mjs",
|
|
94
|
+
"scripts/git-write-endpoint-guard.test.mjs",
|
|
91
95
|
"scripts/question-stop.test.mjs",
|
|
92
96
|
"scripts/background-result-body.test.mjs",
|
|
93
97
|
"scripts/question-request-size.test.mjs",
|
|
@@ -119,10 +123,12 @@
|
|
|
119
123
|
"scripts/styles-no-dangling.test.mjs",
|
|
120
124
|
"scripts/theme-no-hardcoded-colours.test.mjs",
|
|
121
125
|
"scripts/agent-config-regression.test.mjs",
|
|
126
|
+
"scripts/agents-directory-resilience.test.mjs",
|
|
122
127
|
"scripts/model-filter.test.mjs",
|
|
123
128
|
"scripts/settings-default-model.test.mjs",
|
|
124
129
|
"scripts/provider-registry.test.mjs",
|
|
125
130
|
"scripts/provider-pi-reads.test.mjs",
|
|
131
|
+
"scripts/session-extension-credentials.test.mjs",
|
|
126
132
|
"scripts/active-memory.test.mjs",
|
|
127
133
|
"scripts/knowledge-base.test.mjs",
|
|
128
134
|
"scripts/knowledge-import-path.test.mjs",
|
|
@@ -171,6 +177,7 @@
|
|
|
171
177
|
"scripts/scheduled-tasks-browser.test.mjs",
|
|
172
178
|
"scripts/md-render-browser.test.mjs",
|
|
173
179
|
"scripts/right-sidebar-browser.test.mjs",
|
|
180
|
+
"scripts/rail-workspace-follows-session-browser.test.mjs",
|
|
174
181
|
"scripts/sidebar-tools-browser.test.mjs",
|
|
175
182
|
"scripts/tool-runtime-browser.test.mjs",
|
|
176
183
|
"scripts/session-context-menu-browser.test.mjs",
|
|
@@ -200,6 +207,8 @@
|
|
|
200
207
|
"scripts/composer-resize-browser.test.mjs",
|
|
201
208
|
"scripts/workspace-picker-browser.test.mjs",
|
|
202
209
|
"scripts/optimistic-browser.test.mjs",
|
|
210
|
+
"scripts/stop-while-waiting-browser.test.mjs",
|
|
211
|
+
"scripts/new-session-stays-selected-browser.test.mjs",
|
|
203
212
|
"scripts/active-memory-browser.test.mjs",
|
|
204
213
|
"scripts/knowledge-base-browser.test.mjs",
|
|
205
214
|
"scripts/terminal-browser.test.mjs",
|