@haoyiyin/9router 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/command.ts +4 -2
- package/src/index.ts +19 -2
- package/src/provider.ts +10 -0
package/package.json
CHANGED
package/src/command.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { loadConfig, saveConfig, DEFAULT_BASE } from "./config.ts";
|
|
3
|
-
import { registerProvider } from "./provider.ts";
|
|
3
|
+
import { registerProvider, isStaleCtxError } from "./provider.ts";
|
|
4
4
|
|
|
5
5
|
// Never contains a real key. If a dialog pre-fills this placeholder as an
|
|
6
6
|
// editable default, an exact match is treated as "keep the current key".
|
|
@@ -41,7 +41,9 @@ export function registerCommand(pi: ExtensionAPI): void {
|
|
|
41
41
|
await registerProvider(pi);
|
|
42
42
|
} catch (err) {
|
|
43
43
|
ctx.ui.notify(
|
|
44
|
-
|
|
44
|
+
isStaleCtxError(err)
|
|
45
|
+
? "9Router saved. This extension instance was replaced by a session change/reload; the new instance has re-registered the provider."
|
|
46
|
+
: `9Router saved, but provider re-registration failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
45
47
|
"warning",
|
|
46
48
|
);
|
|
47
49
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import registerFetch from "./fetch.ts";
|
|
3
3
|
import registerSearch from "./search.ts";
|
|
4
4
|
import registerTranscribe from "./transcribe.ts";
|
|
5
|
-
import { registerProvider } from "./provider.ts";
|
|
5
|
+
import { registerProvider, isStaleCtxError } from "./provider.ts";
|
|
6
6
|
import { registerCommand } from "./command.ts";
|
|
7
7
|
|
|
8
8
|
export default async function (pi: ExtensionAPI): Promise<void> {
|
|
@@ -15,14 +15,27 @@ export default async function (pi: ExtensionAPI): Promise<void> {
|
|
|
15
15
|
await registerProvider(pi);
|
|
16
16
|
|
|
17
17
|
let refreshing = false;
|
|
18
|
+
let closed = false;
|
|
18
19
|
const timer = setInterval(
|
|
19
20
|
async () => {
|
|
20
21
|
// ponytail: models refresh by unregister+register; zen cache TTL is
|
|
21
22
|
// 30m so refetch happens naturally. Overlap guard avoids re-entrancy.
|
|
22
|
-
if (refreshing) return;
|
|
23
|
+
if (closed || refreshing) return;
|
|
23
24
|
refreshing = true;
|
|
24
25
|
try {
|
|
25
26
|
await registerProvider(pi);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
// The captured `pi` goes stale after session replacement or
|
|
29
|
+
// extension reload, and pi's assertActive() throw must never
|
|
30
|
+
// escape this callback — it would crash the whole process as an
|
|
31
|
+
// uncaughtException. The new session's extension instance
|
|
32
|
+
// re-registers the provider itself, so stop this timer for good.
|
|
33
|
+
if (isStaleCtxError(err)) {
|
|
34
|
+
closed = true;
|
|
35
|
+
clearInterval(timer);
|
|
36
|
+
}
|
|
37
|
+
// Any other error (network hiccup, bad upstream payload) is
|
|
38
|
+
// swallowed: the next tick retries.
|
|
26
39
|
} finally {
|
|
27
40
|
refreshing = false;
|
|
28
41
|
}
|
|
@@ -32,4 +45,8 @@ export default async function (pi: ExtensionAPI): Promise<void> {
|
|
|
32
45
|
if (typeof (timer as { unref?: () => void }).unref === "function") {
|
|
33
46
|
(timer as { unref: () => void }).unref();
|
|
34
47
|
}
|
|
48
|
+
pi.on("session_shutdown", () => {
|
|
49
|
+
closed = true;
|
|
50
|
+
clearInterval(timer);
|
|
51
|
+
});
|
|
35
52
|
}
|
package/src/provider.ts
CHANGED
|
@@ -69,6 +69,16 @@ export function getReasoning(model: RouterModel, devModel?: ModelsDevModel): boo
|
|
|
69
69
|
return /reasoner|thinking|xhigh|high|max|pro|codex|opus|sonnet/i.test(model.id ?? "");
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* True if `err` is pi's stale-extension-ctx error, thrown when a captured
|
|
74
|
+
* `pi`/command ctx is used after session replacement (newSession / fork /
|
|
75
|
+
* switchSession) or extension reload. A stale instance can never recover —
|
|
76
|
+
* the new session's extension instance re-registers everything itself.
|
|
77
|
+
*/
|
|
78
|
+
export function isStaleCtxError(err: unknown): boolean {
|
|
79
|
+
return err instanceof Error && /stale after session replacement|ctx is stale/.test(err.message);
|
|
80
|
+
}
|
|
81
|
+
|
|
72
82
|
export async function registerProvider(pi: ExtensionAPI): Promise<void> {
|
|
73
83
|
const cfg = loadConfig();
|
|
74
84
|
|