@code-yeongyu/senpi 2026.11.10-3 → 2026.11.11
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 +29 -0
- package/dist/bundle/chunks/{chunk-Z2C5N2LM.js → chunk-5F2XC6UC.js} +2 -2
- package/dist/bundle/chunks/{chunk-H3VJVKUK.js → chunk-5PIZWSNG.js} +2 -2
- package/dist/bundle/chunks/{chunk-DEQLHN2J.js → chunk-BPEA5SWW.js} +1 -1
- package/dist/bundle/chunks/{chunk-DIHIEJFV.js → chunk-BUDJLVMF.js} +1 -1
- package/dist/bundle/chunks/{chunk-47T6YMI2.js → chunk-IBDAV2AL.js} +1 -1
- package/dist/bundle/chunks/{chunk-O5WJVIEU.js → chunk-J4ADZ2OK.js} +1 -1
- package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
- package/dist/bundle/chunks/{chunk-HZDKQOWB.js → chunk-L6DF5G6K.js} +1 -1
- package/dist/bundle/chunks/{chunk-E266ORGA.js → chunk-LJFQYCFH.js} +1 -1
- package/dist/bundle/chunks/{chunk-DHAAWL2H.js → chunk-NRZAJUUO.js} +1 -1
- package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
- package/dist/bundle/chunks/{chunk-AC6MDRAE.js → chunk-PUZDKFRT.js} +81 -81
- package/dist/bundle/chunks/{chunk-6Q7Y2G5A.js → chunk-SANWO37B.js} +1 -1
- package/dist/bundle/chunks/{chunk-NN7NESW2.js → chunk-SS5VTBAV.js} +1 -1
- package/dist/bundle/chunks/{chunk-JSD6O3RJ.js → chunk-XUDKT4UH.js} +1 -1
- package/dist/bundle/chunks/{cli-main-ZTT3N4DO.js → cli-main-7II7GCIR.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-5KHVGSCY.js → credential-accounts-N3B5YJS7.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{hand-entry-UV5XPMBF.js → hand-entry-ONOLRMVB.js} +1 -1
- package/dist/bundle/chunks/{host-command-BZN7WYYV.js → host-command-KWMJLPBS.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-TNHASSFJ.js → host-lifecycle-4WQDCN3Y.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +4 -4
- package/dist/bundle/chunks/{host-session-command-O5WYHZXH.js → host-session-command-ZDYEKCUB.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-OCU4NDPC.js → interactive-mode-EPE4M32G.js} +1 -1
- package/dist/bundle/chunks/{main-CNDP6HCF.js → main-OM7BKVZG.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-ODGJAGJP.js → multi-session-host-DB6XMY53.js} +2 -2
- package/dist/bundle/chunks/{package-manager-cli-HSPUG4RJ.js → package-manager-cli-GLU5Q2KV.js} +1 -1
- package/dist/bundle/chunks/{rpc-mode-KOZPXU3U.js → rpc-mode-SDJVBT36.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-SG44FE23.js → schedule-command-5SQD7M5N.js} +1 -1
- package/dist/bundle/chunks/{session-control-endpoint-5A24JYPE.js → session-control-endpoint-R25A2QGS.js} +1 -1
- package/dist/bundle/chunks/{session-picker-73YRS3GE.js → session-picker-GZM5CVQL.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +88 -88
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +4 -4
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/bundle/runtime-manifest.json +1 -1
- package/dist/core/agent-session.d.ts +2 -0
- package/dist/core/credential-accounts.d.ts +2 -0
- package/dist/core/credential-accounts.js +10 -1
- package/dist/core/dynamic-prompt/intent-gate.js +2 -2
- package/dist/core/dynamic-prompt/style.js +2 -2
- package/dist/core/dynamic-prompt/verification.js +2 -2
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-account-test.js +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.d.ts +3 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-connection-test.js +47 -16
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.d.ts +1 -1
- package/dist/core/extensions/builtin/anthropic-subscription/claude-cli-login-slots.js +4 -2
- package/dist/core/extensions/builtin/anthropic-subscription/errors.d.ts +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/errors.js +14 -5
- package/dist/core/extensions/builtin/anthropic-subscription/oauth-login.js +3 -3
- package/dist/core/extensions/builtin/goal/continuation.d.ts +6 -0
- package/dist/core/extensions/builtin/goal/continuation.js +3 -2
- package/dist/core/extensions/builtin/goal/index.js +10 -26
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +0 -6
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +3 -37
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
- package/dist/core/extensions/builtin/goal/provider-retry.d.ts +9 -0
- package/dist/core/extensions/builtin/goal/provider-retry.js +16 -0
- package/dist/core/extensions/builtin/goal/reload-reengagement.d.ts +3 -2
- package/dist/core/extensions/builtin/goal/reload-reengagement.js +4 -4
- package/dist/core/extensions/builtin/goal/session-start-continuation.d.ts +28 -0
- package/dist/core/extensions/builtin/goal/session-start-continuation.js +75 -0
- package/dist/core/extensions/builtin/goal/store.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/store.js +1 -1
- package/dist/core/extensions/builtin/handoff-hint/index.js +15 -11
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.d.ts +16 -0
- package/dist/core/extensions/builtin/handoff-hint/replacement-relay.js +17 -0
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +5 -4
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-haiku-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/claude-opus-5.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/claude-sonnet-5-5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/deepseek-v4-1-flash.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.5.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +3 -3
- package/dist/core/extensions/builtin/prompt-preset/grok-4.5.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +5 -5
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3-1.js +4 -4
- package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +7 -6
- package/dist/core/resource-loader.js +19 -3
- package/dist/core/retry-fallback/controller-types.d.ts +2 -0
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/cooldown.d.ts +7 -0
- package/dist/core/retry-fallback/cooldown.js +13 -1
- package/dist/core/retry-fallback/credential-recovery.d.ts +7 -0
- package/dist/core/retry-fallback/credential-recovery.js +24 -0
- package/dist/core/retry-fallback/settings.d.ts +8 -5
- package/dist/core/retry-fallback/settings.js +9 -6
- package/dist/modes/rpc/delegation-declaration.js +1 -1
- package/dist/modes/rpc/host-gc-evidence.d.ts +1 -1
- package/dist/modes/rpc/host-gc-evidence.js +9 -3
- package/dist/modes/rpc/host-gc.js +4 -1
- package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
- package/dist/modes/rpc/session-attachment-gate.js +3 -0
- package/dist/private-fd-startup-fillers.d.ts +2 -2
- package/dist/private-fd-startup-fillers.js +34 -4
- package/docs/rpc.md +2 -1
- package/docs/settings.md +1 -1
- package/native/socket-secret/prebuilds/win32-arm64/senpi_socket_secret.win32-arm64.node +0 -0
- package/native/socket-secret/prebuilds/win32-x64/senpi_socket_secret.win32-x64.node +0 -0
- package/package.json +6 -6
|
@@ -29,4 +29,28 @@ export async function releaseOnCredentialChange(state, original, deps, unusable)
|
|
|
29
29
|
trigger: "sign-in",
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* A billing pin a usage limit caused holds only until the reset that limit stated: the account serves again
|
|
34
|
+
* then, so the pin and the original's cooldown, unusable mark and circuit are released and the existing
|
|
35
|
+
* revert path tries it once, with no re-login (senpi#4085). A pin with no stated reset (an empty credit
|
|
36
|
+
* balance) still holds until a sign-in. A still-spent original falls back and pins again.
|
|
37
|
+
*/
|
|
38
|
+
export function releaseOnLimitReset(state, original, deps, unusable) {
|
|
39
|
+
if (!state.pinnedByBilling || state.pinnedUntil === undefined)
|
|
40
|
+
return;
|
|
41
|
+
if (deps.cooldowns.wallClock() < state.pinnedUntil)
|
|
42
|
+
return;
|
|
43
|
+
state.pinnedByBilling = false;
|
|
44
|
+
state.pinnedUntil = undefined;
|
|
45
|
+
state.pinned = state.pinnedByRefusal;
|
|
46
|
+
const selector = formatSelector(original);
|
|
47
|
+
deps.cooldowns.clearModel(selector);
|
|
48
|
+
unusable.clear(original);
|
|
49
|
+
deps.circuits?.close(selector);
|
|
50
|
+
deps.logger.info("credential_recovery_released", {
|
|
51
|
+
chainKey: state.chainKey,
|
|
52
|
+
originalSelector: state.originalSelector,
|
|
53
|
+
trigger: "limit-reset",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
32
56
|
//# sourceMappingURL=credential-recovery.js.map
|
|
@@ -33,11 +33,14 @@ export interface ResolvedRetryFallbackSettings {
|
|
|
33
33
|
* follows Fable 5 whichever provider serves it - the builtin Anthropic provider, the
|
|
34
34
|
* Anthropic Subscription extension, a gateway, or Bedrock.
|
|
35
35
|
*
|
|
36
|
-
* The ladder never leaves
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* The ladder never leaves Anthropic. The previous shipped default was removed because it
|
|
37
|
+
* led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude session onto another
|
|
38
|
+
* vendor mid-turn and could rank a guaranteed-refusal OAuth lane first; an Anthropic-only
|
|
39
|
+
* ladder carries neither problem. There is still deliberately no wildcard lane.
|
|
40
|
+
*
|
|
41
|
+
* Opus 5.5 steps to Fable 5.2 before Opus 5: Anthropic meters Opus on its own quota window,
|
|
42
|
+
* so a rate limit on Opus 5.5 also holds every other Opus rung on that account or gateway
|
|
43
|
+
* pool (senpi#6385).
|
|
41
44
|
*
|
|
42
45
|
* Rungs stay on current-generation Opus: a Claude session must never step down to
|
|
43
46
|
* `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
|
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
* follows Fable 5 whichever provider serves it - the builtin Anthropic provider, the
|
|
5
5
|
* Anthropic Subscription extension, a gateway, or Bedrock.
|
|
6
6
|
*
|
|
7
|
-
* The ladder never leaves
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* The ladder never leaves Anthropic. The previous shipped default was removed because it
|
|
8
|
+
* led with cross-family rungs (`k3`, `kimi-k3`), which moved a Claude session onto another
|
|
9
|
+
* vendor mid-turn and could rank a guaranteed-refusal OAuth lane first; an Anthropic-only
|
|
10
|
+
* ladder carries neither problem. There is still deliberately no wildcard lane.
|
|
11
|
+
*
|
|
12
|
+
* Opus 5.5 steps to Fable 5.2 before Opus 5: Anthropic meters Opus on its own quota window,
|
|
13
|
+
* so a rate limit on Opus 5.5 also holds every other Opus rung on that account or gateway
|
|
14
|
+
* pool (senpi#6385).
|
|
12
15
|
*
|
|
13
16
|
* Rungs stay on current-generation Opus: a Claude session must never step down to
|
|
14
17
|
* `claude-opus-4-8` or `claude-opus-4-6` (senpi#3727).
|
|
@@ -18,7 +21,7 @@ export const DEFAULT_FALLBACK_CHAINS = {
|
|
|
18
21
|
"claude-fable-5-2": ["claude-opus-5-5", "claude-opus-5"],
|
|
19
22
|
"claude-fable-5-1": ["claude-opus-5-5", "claude-opus-5"],
|
|
20
23
|
"claude-fable-5": ["claude-opus-5-5", "claude-opus-5"],
|
|
21
|
-
"claude-opus-5-5": ["claude-opus-5"],
|
|
24
|
+
"claude-opus-5-5": ["claude-fable-5-2", "claude-opus-5"],
|
|
22
25
|
};
|
|
23
26
|
function cloneDefaultFallbackChains() {
|
|
24
27
|
const chains = {};
|
|
@@ -54,7 +54,7 @@ export function samePrincipal(a, b) {
|
|
|
54
54
|
function parseRoles(value) {
|
|
55
55
|
if (value === undefined)
|
|
56
56
|
return undefined;
|
|
57
|
-
const invalid = "roles holds each of interactive_user, task_parent at most once";
|
|
57
|
+
const invalid = "roles holds each of interactive_user, task_parent, peer_agent at most once";
|
|
58
58
|
if (!Array.isArray(value))
|
|
59
59
|
return invalid;
|
|
60
60
|
const roles = new Set();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HostDaemonDirectory } from "./host-daemon-paths.ts";
|
|
2
|
-
export type EndpointInUse = "live_generation" | "live_claim" | "reachable";
|
|
2
|
+
export type EndpointInUse = "live_generation" | "live_claim" | "reachable" | "credential_unavailable";
|
|
3
3
|
export type SocketSilence = "socket_refused" | "socket_absent";
|
|
4
4
|
/**
|
|
5
5
|
* The three-part test, in the order its answers are reported: (a) no generation pidfile names a live
|
|
@@ -33,12 +33,17 @@ export async function endpointInUse(paths, socket) {
|
|
|
33
33
|
return { inUse: "live_claim" };
|
|
34
34
|
}
|
|
35
35
|
const silence = await socketSilence(socket);
|
|
36
|
+
if (silence === "credential_unavailable")
|
|
37
|
+
return { inUse: silence };
|
|
36
38
|
if (silence === undefined)
|
|
37
39
|
return { inUse: "reachable" };
|
|
38
40
|
// A successor mid-handoff listens on `<socket>.next-<gen>` before it registers anywhere.
|
|
39
41
|
const successors = (await socketSiblings(socket)).filter((name) => name.startsWith(`${basename(socket)}.next-`) && !isBindScratchName(name));
|
|
40
42
|
for (const successor of successors) {
|
|
41
|
-
|
|
43
|
+
const successorSilence = await socketSilence(join(dirname(socket), successor));
|
|
44
|
+
if (successorSilence === "credential_unavailable")
|
|
45
|
+
return { inUse: successorSilence };
|
|
46
|
+
if (successorSilence === undefined)
|
|
42
47
|
return { inUse: "reachable" };
|
|
43
48
|
}
|
|
44
49
|
return { inUse: undefined, silence };
|
|
@@ -101,10 +106,11 @@ async function recordIsLive(record) {
|
|
|
101
106
|
async function socketSilence(socket) {
|
|
102
107
|
let secret;
|
|
103
108
|
if (process.platform === "win32") {
|
|
104
|
-
// The pipe name includes the secret
|
|
109
|
+
// The pipe name includes the secret, so without a readable one nothing proves the pipe silent: a
|
|
110
|
+
// live host may still serve it under the key whose file was lost (sisyphuslabs/omo-desktop-app#4199).
|
|
105
111
|
secret = await readSocketSecret(socketSecretPath(socket)).catch(() => undefined);
|
|
106
112
|
if (secret === undefined)
|
|
107
|
-
return "
|
|
113
|
+
return "credential_unavailable";
|
|
108
114
|
}
|
|
109
115
|
return new Promise((resolveProbe) => {
|
|
110
116
|
const connection = createConnection(resolveSocketTransportAddress(socket, process.platform, secret));
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* live_generation a generation pidfile (any, the pointer's included) names a live process
|
|
15
15
|
* live_claim a session-path claim in `reservations/` has a live owner
|
|
16
16
|
* reachable the socket (or a `.next-*` successor bind) did not refuse the connection
|
|
17
|
+
* credential_unavailable win32: the socket secret cannot be read, so its pipe cannot be proven silent
|
|
17
18
|
* locked the ensure lock was not free within 2 s
|
|
18
19
|
* legacy_layout the agent dir predates layout 2; its flat files belong to a legacy host
|
|
19
20
|
* unknown_identity nothing in the directory names its socket, so its lock cannot be taken
|
|
@@ -94,7 +95,9 @@ export async function gcEndpoint(socket, dir, options) {
|
|
|
94
95
|
try {
|
|
95
96
|
await options._test?.afterLockAcquired?.(socket);
|
|
96
97
|
const evidence = await endpointInUse(hostDaemonDirectoryPaths(dir), socket);
|
|
97
|
-
if (evidence.inUse === "live_claim" ||
|
|
98
|
+
if (evidence.inUse === "live_claim" ||
|
|
99
|
+
evidence.inUse === "credential_unavailable" ||
|
|
100
|
+
(evidence.inUse !== undefined && options.staleEngine === undefined)) {
|
|
98
101
|
return { removed: false, reason: evidence.inUse };
|
|
99
102
|
}
|
|
100
103
|
if (evidence.inUse !== undefined)
|
|
@@ -68,6 +68,9 @@ export function attachmentGate(host, command) {
|
|
|
68
68
|
return undefined;
|
|
69
69
|
if (host.sessionsByConnection.get(owner)?.has(command.sessionId))
|
|
70
70
|
return undefined;
|
|
71
|
+
// senpi#6305: the forwarder's peer lane delivers an agent's message without ever attaching; only its admit passes.
|
|
72
|
+
if (command.type === "admit_external_message" && host.connectionRoles.grantedByForwarder(owner, "peer_agent"))
|
|
73
|
+
return undefined;
|
|
71
74
|
if (process.env.SENPI_RPC_REQUIRE_ATTACH === "1")
|
|
72
75
|
return error(command.id, command.type, "session_not_attached", {
|
|
73
76
|
reason: "this connection is not attached to that session",
|
|
@@ -22,8 +22,8 @@ export declare function releaseStartupFillers(child: {
|
|
|
22
22
|
* them, see `withStartupPipe`) and removes their list from the hand-off, so a process this one re-executes or
|
|
23
23
|
* spawns never closes a descriptor that is not a filler. A slot the #4749 private table names is that
|
|
24
24
|
* channel's, never a filler, and stays open. A table that does not decode (the `unparseable` marker included)
|
|
25
|
-
* cannot prove any slot free of a channel, so no filler is closed.
|
|
26
|
-
* receive a hand-off.
|
|
25
|
+
* cannot prove any slot free of a channel, so no filler is closed. A slot that no longer holds its filler is
|
|
26
|
+
* the runtime's own and stays open (`inheritedFillerCheck`). Called first by every entry that can receive a hand-off.
|
|
27
27
|
*/
|
|
28
28
|
export declare function releaseInheritedStartupFillers(env: NodeJS.ProcessEnv): void;
|
|
29
29
|
//# sourceMappingURL=private-fd-startup-fillers.d.ts.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* the release of the ones this process inherited. Side-effect free and never imports `private-fds.ts`: the
|
|
4
4
|
* startup stub releases its fillers before that module adopts the table at import.
|
|
5
5
|
*/
|
|
6
|
-
import { closeSync } from "node:fs";
|
|
6
|
+
import { closeSync, fstatSync } from "node:fs";
|
|
7
7
|
import { HOST_STARTUP_EVENTS_ENV } from "./modes/rpc/host-identity-env.js";
|
|
8
8
|
import { encodeStartupHandOff, parseStartupHandOff, STARTUP_EVENT_FD } from "./modes/rpc/host-startup-wire.js";
|
|
9
9
|
import { decodePrivateFdTable, PRIVATE_FDS_TABLE_ENV } from "./private-fd-table.js";
|
|
@@ -53,13 +53,42 @@ function namedPrivateSlots(encoded) {
|
|
|
53
53
|
slots.add(fd);
|
|
54
54
|
return slots;
|
|
55
55
|
}
|
|
56
|
+
function statOf(fd) {
|
|
57
|
+
try {
|
|
58
|
+
return fstatSync(fd);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const sameFile = (a, b) => a.dev === b.dev && a.ino === b.ino;
|
|
65
|
+
/**
|
|
66
|
+
* Whether a listed slot still holds the filler the spawn put there. On POSIX a "pipe" slot is an AF_UNIX socketpair
|
|
67
|
+
* end under Node and Bun, and neither runtime holds a socket of its own this early; the startup pipe the same spawn
|
|
68
|
+
* placed at `STARTUP_EVENT_FD` must still be there too. A process that replaced its image between the spawn and this
|
|
69
|
+
* entry lost both, because Bun marks every inherited descriptor from 4 up close-on-exec at startup, and its runtime
|
|
70
|
+
* reuses the numbers: the compiled launcher's trust exec left Bun's own copies of stdout and stderr at 5 and 6, and
|
|
71
|
+
* closing them by number failed the host's first stderr write with EBADF (senpi#6282). Windows keeps the plain list.
|
|
72
|
+
*/
|
|
73
|
+
function inheritedFillerCheck() {
|
|
74
|
+
if (process.platform === "win32")
|
|
75
|
+
return () => true;
|
|
76
|
+
const startup = statOf(STARTUP_EVENT_FD);
|
|
77
|
+
if (!startup?.isSocket())
|
|
78
|
+
return () => false;
|
|
79
|
+
const kept = [startup, ...[0, 1, 2].map(statOf).filter((stat) => stat !== undefined)];
|
|
80
|
+
return (slot) => {
|
|
81
|
+
const stat = statOf(slot);
|
|
82
|
+
return stat !== undefined && stat.isSocket() && !kept.some((other) => sameFile(stat, other));
|
|
83
|
+
};
|
|
84
|
+
}
|
|
56
85
|
/**
|
|
57
86
|
* Closes the filler pipes this process inherited (they only kept libuv from aborting the spawn that created
|
|
58
87
|
* them, see `withStartupPipe`) and removes their list from the hand-off, so a process this one re-executes or
|
|
59
88
|
* spawns never closes a descriptor that is not a filler. A slot the #4749 private table names is that
|
|
60
89
|
* channel's, never a filler, and stays open. A table that does not decode (the `unparseable` marker included)
|
|
61
|
-
* cannot prove any slot free of a channel, so no filler is closed.
|
|
62
|
-
* receive a hand-off.
|
|
90
|
+
* cannot prove any slot free of a channel, so no filler is closed. A slot that no longer holds its filler is
|
|
91
|
+
* the runtime's own and stays open (`inheritedFillerCheck`). Called first by every entry that can receive a hand-off.
|
|
63
92
|
*/
|
|
64
93
|
export function releaseInheritedStartupFillers(env) {
|
|
65
94
|
const handOff = parseStartupHandOff(env[HOST_STARTUP_EVENTS_ENV]);
|
|
@@ -67,8 +96,9 @@ export function releaseInheritedStartupFillers(env) {
|
|
|
67
96
|
return;
|
|
68
97
|
const privateSlots = namedPrivateSlots(env[PRIVATE_FDS_TABLE_ENV]);
|
|
69
98
|
if (privateSlots) {
|
|
99
|
+
const inherited = inheritedFillerCheck();
|
|
70
100
|
for (const slot of handOff.fillers) {
|
|
71
|
-
if (privateSlots.has(slot))
|
|
101
|
+
if (privateSlots.has(slot) || !inherited(slot))
|
|
72
102
|
continue;
|
|
73
103
|
try {
|
|
74
104
|
closeSync(slot);
|
package/docs/rpc.md
CHANGED
|
@@ -2263,7 +2263,8 @@ which prints the bare socket path).
|
|
|
2263
2263
|
`reason` is `socket_refused` or `socket_absent`. A sibling that is a directory is not unlinked: it stays and
|
|
2264
2264
|
the removed entry lists it as `skipped: [{ path, type: "directory" }]`. Because the directory goes last, a
|
|
2265
2265
|
removal that fails part-way leaves the endpoint listed for the next gc. Everything else is kept with its
|
|
2266
|
-
reason: `live_generation`, `live_claim`, `reachable`, `
|
|
2266
|
+
reason: `live_generation`, `live_claim`, `reachable`, `credential_unavailable` (Windows: the socket secret
|
|
2267
|
+
is missing or invalid, so the pipe it names cannot be proven silent), `locked` (an ensure held the lock for 2 s),
|
|
2267
2268
|
`legacy_layout` (a flat directory without `layout.json` - a legacy host's, never touched), `unknown_identity`
|
|
2268
2269
|
(nothing names the socket, so its lock cannot be taken), or `failed` (reading the evidence or removing threw;
|
|
2269
2270
|
the entry carries `error`) - and a `failed` endpoint never stops gc from going on to the others.
|
package/docs/settings.md
CHANGED
|
@@ -332,7 +332,7 @@ Keep `retry.provider.maxRetries` at `0` unless provider-level retries are explic
|
|
|
332
332
|
|
|
333
333
|
#### Model fallback chains
|
|
334
334
|
|
|
335
|
-
`retry.fallbackChains` maps a primary-model selector to an ordered list of fallback selectors. A selector is `provider/model` with an optional `:thinking-level` suffix, or a bare `model` id that applies to that model vendor's own providers - for example `anthropic-subscription` and `anthropic` for Claude, `chatgpt-subscription` and `openai` for GPT, `google` and `google-vertex` for Gemini. A bare selector never expands onto a reseller, gateway, cloud re-host or custom provider that also serves the model; name one as `provider/model` to use it. A bare id from a vendor senpi does not know stays on the provider the session is already using. Within the vendor's providers, those holding an OAuth credential are preferred, then a fixed precedence order. Senpi ships bare default chains for `claude-fable-5-2`, `claude-fable-5-1` and `claude-fable-5` - `claude-opus-5-5`, then `claude-opus-5` - and for `claude-opus-5-5` - `claude-opus-5
|
|
335
|
+
`retry.fallbackChains` maps a primary-model selector to an ordered list of fallback selectors. A selector is `provider/model` with an optional `:thinking-level` suffix, or a bare `model` id that applies to that model vendor's own providers - for example `anthropic-subscription` and `anthropic` for Claude, `chatgpt-subscription` and `openai` for GPT, `google` and `google-vertex` for Gemini. A bare selector never expands onto a reseller, gateway, cloud re-host or custom provider that also serves the model; name one as `provider/model` to use it. A bare id from a vendor senpi does not know stays on the provider the session is already using. Within the vendor's providers, those holding an OAuth credential are preferred, then a fixed precedence order. Senpi ships bare default chains for `claude-fable-5-2`, `claude-fable-5-1` and `claude-fable-5` - `claude-opus-5-5`, then `claude-opus-5` - and for `claude-opus-5-5` - `claude-fable-5-2`, then `claude-opus-5`, because a rate limit on one Opus model holds the whole Opus quota - with no thinking level of their own, so a shipped rung inherits the session's thinking level (clamped to what the model supports), so Fable 5.2, Fable 5.1, Fable 5 and Opus 5.5 keep a fallback chain whichever provider serves them, and a fallback never leaves the Anthropic family; set a key to `[]` to opt out entirely, or set one `provider/claude-fable-5-1` key to override just that provider. For example, this switches Fable 5.1 to Kimi K3 at `max` thinking when an eligible failure occurs:
|
|
336
336
|
|
|
337
337
|
```json
|
|
338
338
|
{
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi",
|
|
3
|
-
"version": "2026.11.
|
|
3
|
+
"version": "2026.11.11",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {
|
|
94
94
|
"@earendil-works/chord": "0.99.1",
|
|
95
|
-
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.
|
|
96
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.
|
|
97
|
-
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.
|
|
95
|
+
"@earendil-works/pi-agent-core": "npm:@code-yeongyu/senpi-agent-core@2026.11.11",
|
|
96
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.11",
|
|
97
|
+
"@earendil-works/pi-tui": "npm:@code-yeongyu/senpi-tui@2026.11.11",
|
|
98
98
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
99
99
|
"ajv": "8.20.0",
|
|
100
100
|
"chalk": "6.0.0",
|
|
@@ -115,8 +115,8 @@
|
|
|
115
115
|
"yaml": "2.9.1",
|
|
116
116
|
"@anthropic-ai/claude-agent-sdk": "0.3.321",
|
|
117
117
|
"@anthropic-ai/sdk": "0.127.0",
|
|
118
|
-
"@code-yeongyu/senpi-codemode": "2026.11.
|
|
119
|
-
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.
|
|
118
|
+
"@code-yeongyu/senpi-codemode": "2026.11.11",
|
|
119
|
+
"@earendil-works/pi-pty": "npm:@code-yeongyu/senpi-pty@2026.11.11",
|
|
120
120
|
"@modelcontextprotocol/sdk": "1.32.1",
|
|
121
121
|
"@mozilla/readability": "0.6.0",
|
|
122
122
|
"linkedom": "0.18.13",
|