@code-yeongyu/senpi 2026.11.10-1 → 2026.11.10-3
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 +42 -0
- package/dist/bundle/chunks/{args-77GYOPEO.js → args-HFYB6URH.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YFZ734T3.js → azure-openai-responses-MNN2TINI.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZUQCYIRY.js → chunk-2ODGHBUD.js} +1 -1
- package/dist/bundle/chunks/{chunk-YPAQXRP3.js → chunk-47T6YMI2.js} +2 -2
- package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
- package/dist/bundle/chunks/{chunk-MSDBBOB2.js → chunk-5H43O4MA.js} +1 -1
- package/dist/bundle/chunks/{chunk-3IYWH6RO.js → chunk-6Q7Y2G5A.js} +1 -1
- package/dist/bundle/chunks/{chunk-X4XWAB3J.js → chunk-AC6MDRAE.js} +81 -81
- package/dist/bundle/chunks/{chunk-33MZJTQ3.js → chunk-B4WD5XHP.js} +1 -1
- package/dist/bundle/chunks/{chunk-V5FSENW6.js → chunk-COKX65TE.js} +1 -1
- package/dist/bundle/chunks/{chunk-LHEP3ACI.js → chunk-DEQLHN2J.js} +2 -2
- package/dist/bundle/chunks/{chunk-EJB5E2JK.js → chunk-DHAAWL2H.js} +1 -1
- package/dist/bundle/chunks/{chunk-2EJ4BGBM.js → chunk-DHORIIVQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-GPXZTSEX.js → chunk-DIHIEJFV.js} +3 -3
- package/dist/bundle/chunks/{chunk-NH7SYIGN.js → chunk-DLPV4B3R.js} +1 -1
- package/dist/bundle/chunks/{chunk-RCHIMH7N.js → chunk-DQY26AX3.js} +1 -1
- package/dist/bundle/chunks/{chunk-Y7Z7XPUF.js → chunk-DZQA5LVN.js} +1 -1
- package/dist/bundle/chunks/{chunk-2CNSCMYC.js → chunk-E5O4SP5I.js} +1 -1
- package/dist/bundle/chunks/{chunk-3Q5ZENSD.js → chunk-E6ZBTSC6.js} +1 -1
- package/dist/bundle/chunks/{chunk-HI5JSLG5.js → chunk-H3VJVKUK.js} +4 -4
- package/dist/bundle/chunks/{chunk-L6NRDUWJ.js → chunk-HZDKQOWB.js} +1 -1
- package/dist/bundle/chunks/chunk-IREUIMFE.js +11 -0
- package/dist/bundle/chunks/{chunk-FAFIGBUD.js → chunk-JSD6O3RJ.js} +2 -2
- package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-L3KVP7VJ.js} +3 -3
- package/dist/bundle/chunks/{chunk-7MN7YNO5.js → chunk-L7DGTA3L.js} +1 -1
- package/dist/bundle/chunks/{chunk-J2NGJKXL.js → chunk-LXINMDMX.js} +1 -1
- package/dist/bundle/chunks/{chunk-H33A33M4.js → chunk-M2VJVJMK.js} +1 -1
- package/dist/bundle/chunks/{chunk-ARUV3US4.js → chunk-NN7NESW2.js} +1 -1
- package/dist/bundle/chunks/{chunk-2U6HXWXS.js → chunk-NNQ34ZDW.js} +1 -1
- package/dist/bundle/chunks/chunk-NQKCJQHB.js +2 -0
- package/dist/bundle/chunks/{chunk-TGW4R7SI.js → chunk-O5WJVIEU.js} +1 -1
- package/dist/bundle/chunks/{chunk-O3FG6HML.js → chunk-PJI5GT7U.js} +1 -1
- package/dist/bundle/chunks/{chunk-BPNSOZ2C.js → chunk-QUVSIW2R.js} +1 -1
- package/dist/bundle/chunks/{chunk-WXYMAUFX.js → chunk-QX7WD3IM.js} +1 -1
- package/dist/bundle/chunks/{chunk-Y4ONXQMR.js → chunk-RGHVPUUX.js} +1 -1
- package/dist/bundle/chunks/{chunk-RYPM5BZQ.js → chunk-S7NAJQB3.js} +1 -1
- package/dist/bundle/chunks/{chunk-LT353NSF.js → chunk-T4CUD7RF.js} +1 -1
- package/dist/bundle/chunks/{chunk-J7FW7MED.js → chunk-TCZORL3V.js} +1 -1
- package/dist/bundle/chunks/{chunk-KU72IKV5.js → chunk-VGSWOLIY.js} +1 -1
- package/dist/bundle/chunks/{chunk-DBQBJTMF.js → chunk-VUY4BZDI.js} +1 -1
- package/dist/bundle/chunks/chunk-XI6THO4F.js +2 -0
- package/dist/bundle/chunks/{chunk-THYEZBUG.js → chunk-Z2C5N2LM.js} +2 -2
- package/dist/bundle/chunks/{cli-main-VG3C2EXI.js → cli-main-ZTT3N4DO.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-LVVPKFEY.js → credential-accounts-5KHVGSCY.js} +1 -1
- package/dist/bundle/chunks/{dispatch-EP35KKJ3.js → dispatch-YUYZQXRP.js} +1 -1
- package/dist/bundle/chunks/{hand-entry-3LQKUTYS.js → hand-entry-UV5XPMBF.js} +3 -3
- package/dist/bundle/chunks/{headless-host-HKGNJGYT.js → headless-host-53UMJMM6.js} +1 -1
- package/dist/bundle/chunks/{health-write-FBF74QJ7.js → health-write-JLSF5EVV.js} +1 -1
- package/dist/bundle/chunks/{help-fast-path-5SCPY3OH.js → help-fast-path-552QLMWT.js} +1 -1
- package/dist/bundle/chunks/{host-command-CCASS7W4.js → host-command-BZN7WYYV.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-WWC7HXQ6.js → host-lifecycle-TNHASSFJ.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +1 -1
- package/dist/bundle/chunks/{host-session-command-YJB2FLKU.js → host-session-command-O5WYHZXH.js} +1 -1
- package/dist/bundle/chunks/{host-socket-request-DMYXC56U.js → host-socket-request-QN4Q2P5E.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-CUN3WMUF.js → interactive-mode-OCU4NDPC.js} +1 -1
- package/dist/bundle/chunks/{login-health-EVXVLHB2.js → login-health-TOSQT5RG.js} +1 -1
- package/dist/bundle/chunks/{main-P7XRC2OI.js → main-CNDP6HCF.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-244X3P6L.js → multi-session-host-ODGJAGJP.js} +11 -8
- package/dist/bundle/chunks/openai-codex-responses-VRGOAFEG.js +10 -0
- package/dist/bundle/chunks/{openai-responses-NIVBWE2H.js → openai-responses-I7PXRWCM.js} +2 -2
- package/dist/bundle/chunks/{package-manager-cli-UVWJOQA3.js → package-manager-cli-HSPUG4RJ.js} +1 -1
- package/dist/bundle/chunks/{role-HQUN4XXK.js → role-AEOLJRTP.js} +1 -1
- package/dist/bundle/chunks/{rotation-stream-F3BEMXBE.js → rotation-stream-E3FJFHXJ.js} +1 -1
- package/dist/bundle/chunks/rpc-mode-KOZPXU3U.js +2 -0
- package/dist/bundle/chunks/{rpc-monitor-source-C6VQ3BC4.js → rpc-monitor-source-PGTDSGWP.js} +1 -1
- package/dist/bundle/chunks/{run-attached-tui-R3RB5P7G.js → run-attached-tui-4AENRP7P.js} +1 -1
- package/dist/bundle/chunks/{schedule-command-5FG2OKSR.js → schedule-command-SG44FE23.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-5A24JYPE.js +3 -0
- package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-73YRS3GE.js} +1 -1
- package/dist/bundle/chunks/{session-slot-CZGQQCOC.js → session-slot-RH7JLO3F.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +199 -196
- package/dist/bundle/chunks/{state-store-S7QAESIF.js → state-store-IECVTYXC.js} +1 -1
- package/dist/bundle/cli.js +2 -2
- package/dist/bundle/host-supervisor.js +1 -1
- 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-runtime.d.ts +2 -0
- package/dist/core/agent-session-runtime.js +5 -0
- package/dist/core/agent-session.d.ts +13 -3
- package/dist/core/agent-session.js +89 -18
- package/dist/core/assistant-error-redaction.d.ts +9 -0
- package/dist/core/assistant-error-redaction.js +27 -0
- package/dist/core/config-reference-redaction.js +90 -10
- package/dist/core/context-files.d.ts +10 -0
- package/dist/core/context-files.js +58 -12
- package/dist/core/egress/wire.d.ts +13 -0
- package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
- package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.d.ts +43 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-grant.js +196 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +4 -17
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +64 -75
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.d.ts +1 -1
- package/dist/core/extensions/builtin/mcp/auth/oauth-refresh.js +25 -4
- package/dist/core/extensions/builtin/mcp/auth/oauth.js +10 -7
- package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +18 -0
- package/dist/core/extensions/builtin/mcp/auth/token-store.js +3 -0
- package/dist/core/extensions/builtin/mcp/catalog-cache.js +7 -6
- package/dist/core/extensions/builtin/mcp/catalog.js +3 -2
- package/dist/core/extensions/builtin/mcp/invocation.js +6 -0
- package/dist/core/extensions/builtin/mcp/service-register.js +16 -2
- package/dist/core/extensions/builtin/mcp/service-tools-changed.js +61 -54
- package/dist/core/extensions/builtin/mcp/startup-race.d.ts +2 -0
- package/dist/core/extensions/builtin/mcp/startup-race.js +25 -7
- package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-2.js +6 -6
- package/dist/core/extensions/loader.js +24 -4
- package/dist/core/extensions/runner.d.ts +9 -1
- package/dist/core/extensions/runner.js +9 -4
- package/dist/core/extensions/types.d.ts +17 -3
- package/dist/core/hand/hook-advertise.d.ts +41 -0
- package/dist/core/hand/hook-advertise.js +98 -0
- package/dist/core/hand/hook-contract.d.ts +66 -0
- package/dist/core/hand/hook-contract.js +138 -0
- package/dist/core/open-timing.d.ts +2 -0
- package/dist/core/open-timing.js +7 -1
- package/dist/core/resource-loader.js +2 -0
- package/dist/core/retry-fallback/log.d.ts +7 -1
- package/dist/core/retry-fallback/log.js +82 -5
- package/dist/core/retry-fallback/model-config-error.js +2 -1
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.js +15 -1
- package/dist/core/settings-manager.d.ts +6 -0
- package/dist/core/settings-manager.js +8 -0
- package/dist/core/turn-origin.d.ts +19 -13
- package/dist/core/turn-origin.js +34 -30
- package/dist/core/ultrafast-lanes.d.ts +1 -0
- package/dist/core/ultrafast-lanes.js +4 -2
- package/dist/modes/interactive/components/footer.d.ts +23 -5
- package/dist/modes/interactive/components/footer.js +45 -16
- package/dist/modes/interactive/interactive-mode.js +9 -1
- package/dist/modes/interactive/session-control-rpc-commands.d.ts +1 -1
- package/dist/modes/interactive/session-control-rpc-commands.js +5 -0
- package/dist/modes/rpc/admit-external-message-command.d.ts +2 -2
- package/dist/modes/rpc/admit-external-message-command.js +2 -10
- package/dist/modes/rpc/compact-admission-validation.js +4 -1
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +20 -8
- package/dist/modes/rpc/connection-roles.d.ts +2 -0
- package/dist/modes/rpc/connection-roles.js +4 -0
- package/dist/modes/rpc/custom-capability.d.ts +4 -0
- package/dist/modes/rpc/custom-capability.js +4 -0
- package/dist/modes/rpc/delegated-drive-origin.d.ts +6 -6
- package/dist/modes/rpc/delegated-drive-origin.js +7 -8
- package/dist/modes/rpc/delegation-declaration.d.ts +2 -0
- package/dist/modes/rpc/delegation-declaration.js +5 -1
- package/dist/modes/rpc/effect-commands.d.ts +21 -0
- package/dist/modes/rpc/host-capabilities.d.ts +1 -0
- package/dist/modes/rpc/host-capabilities.js +5 -1
- package/dist/modes/rpc/host-observe-request.d.ts +2 -0
- package/dist/modes/rpc/host-observe-request.js +6 -1
- package/dist/modes/rpc/host-park-ledger.d.ts +19 -0
- package/dist/modes/rpc/host-park-ledger.js +110 -0
- package/dist/modes/rpc/lifecycle-delivery.js +4 -1
- package/dist/modes/rpc/loopless-tool-handlers.d.ts +6 -0
- package/dist/modes/rpc/loopless-tool-handlers.js +14 -1
- package/dist/modes/rpc/media-placeholders.js +1 -0
- package/dist/modes/rpc/method-classes-schema-host.d.ts +42 -1
- package/dist/modes/rpc/method-classes-schema-host.js +24 -21
- package/dist/modes/rpc/method-classes-schema.d.ts +42 -1
- package/dist/modes/rpc/method-classes-schema.js +5 -2
- package/dist/modes/rpc/method-classes.d.ts +2 -0
- package/dist/modes/rpc/method-classes.js +3 -0
- package/dist/modes/rpc/multi-session-host.js +1 -1
- package/dist/modes/rpc/rpc-host-command-types.d.ts +14 -1
- package/dist/modes/rpc/rpc-host-response-types.d.ts +21 -0
- package/dist/modes/rpc/session-attachment-gate.js +2 -0
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.js +6 -1
- package/dist/modes/rpc/session-connection-context.d.ts +2 -0
- package/dist/modes/rpc/session-connection-context.js +4 -0
- package/dist/modes/rpc/session-registry-attach.d.ts +2 -0
- package/dist/modes/rpc/session-registry-attach.js +15 -11
- package/dist/modes/rpc/session-registry-claims.js +2 -0
- package/dist/modes/rpc/session-registry-open.js +13 -7
- package/dist/modes/rpc/session-registry-opening.js +2 -1
- package/dist/modes/rpc/session-registry-types.d.ts +4 -0
- package/dist/modes/rpc/session-registry.d.ts +3 -0
- package/dist/modes/rpc/session-registry.js +27 -3
- package/dist/modes/rpc/session-router-close.js +1 -0
- package/dist/modes/rpc/session-router-context.d.ts +4 -2
- package/dist/modes/rpc/session-router-context.js +3 -0
- package/dist/modes/rpc/session-router-host-reads.js +1 -0
- package/dist/modes/rpc/session-router-occupancy.js +10 -1
- package/dist/modes/rpc/session-router-open-result.d.ts +12 -0
- package/dist/modes/rpc/session-router-open-result.js +63 -0
- package/dist/modes/rpc/session-router-open.d.ts +3 -0
- package/dist/modes/rpc/session-router-open.js +15 -63
- package/dist/modes/rpc/session-router-park.d.ts +15 -0
- package/dist/modes/rpc/session-router-park.js +176 -0
- package/dist/modes/rpc/session-router-routing.d.ts +1 -1
- package/dist/modes/rpc/session-router-routing.js +7 -0
- package/dist/modes/rpc/session-status-source.js +2 -2
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/rpc-inbound-samples.json +22 -2
- package/docs/extensions.md +10 -0
- package/docs/rpc.md +50 -0
- package/docs/settings.md +1 -0
- package/docs/usage.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
- package/dist/bundle/chunks/chunk-436DNN4Z.js +0 -2
- package/dist/bundle/chunks/chunk-DLAY7CIU.js +0 -2
- package/dist/bundle/chunks/chunk-XH2MWTXA.js +0 -11
- package/dist/bundle/chunks/openai-codex-responses-ABB2J43U.js +0 -10
- package/dist/bundle/chunks/rpc-mode-G4I3HHPB.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-XWCFLJYC.js +0 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ULTRAFAST_DOCUMENTED_MODEL_IDS } from "@earendil-works/pi-ai";
|
|
1
2
|
/**
|
|
2
3
|
* Providers that receive `service_tier: "ultrafast"`: OpenAI's own API and the ChatGPT
|
|
3
4
|
* subscription backend. Gateways and other providers serving an OpenAI model on the Responses
|
|
@@ -5,7 +6,6 @@
|
|
|
5
6
|
* sends Ultrafast only to first-party OpenAI and Codex models).
|
|
6
7
|
*/
|
|
7
8
|
const ULTRAFAST_PROVIDERS = new Set(["openai", "chatgpt-subscription"]);
|
|
8
|
-
const ULTRAFAST_DOCUMENTED_MODEL_IDS = new Set(["gpt-6-astra", "gpt-6.1-sol"]);
|
|
9
9
|
export function serviceTierForProvider(provider, serviceTier) {
|
|
10
10
|
if (serviceTier !== "ultrafast")
|
|
11
11
|
return serviceTier;
|
|
@@ -18,7 +18,9 @@ export function ultrafastSelectionWarning(model, serviceTier) {
|
|
|
18
18
|
if (!ULTRAFAST_PROVIDERS.has(model.provider)) {
|
|
19
19
|
return `Ultrafast is only sent to OpenAI and ChatGPT Subscription; ${model.provider}/${model.id} runs at its default tier`;
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
// A tier-pinned row (for example `gpt-6.1-sol-ultrafast`) is documented by its upstream model,
|
|
22
|
+
// as the pricing code already checks (senpi#2978).
|
|
23
|
+
if (ULTRAFAST_DOCUMENTED_MODEL_IDS.has(model.upstreamModelId ?? model.id))
|
|
22
24
|
return undefined;
|
|
23
25
|
return `Ultrafast is documented for GPT-6 Astra and GPT-6.1 Sol; ${model.provider}/${model.id} may reject or ignore it`;
|
|
24
26
|
}
|
|
@@ -10,11 +10,26 @@ export declare function formatTokens(count: number): string;
|
|
|
10
10
|
/**
|
|
11
11
|
* Mirrors the provider-shown-only-when->1 rule for accounts: the active account
|
|
12
12
|
* name appears only when the provider actually pools more than one slot. The
|
|
13
|
-
* pick shown is the pin
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* pick shown is the effective pin (CLI > settings > stored, senpi#5983) when
|
|
14
|
+
* present, else the first account of the user order (senpi#3591), else the
|
|
15
|
+
* session's HRW winner - the same ranking the rotation engine uses, so the
|
|
16
|
+
* footer names the slot that will serve. The effective pin only reaches the
|
|
17
|
+
* footer for the Anthropic subscription provider (senpi#6222 review); other
|
|
18
|
+
* pooled providers keep their stored pin even when a slot shares the pin's
|
|
19
|
+
* name. A blocked pin (blockedUntil in the
|
|
20
|
+
* future) is skipped for the first usable slot in that ranking, and the
|
|
21
|
+
* suffix marks the blocked pin (`@B (A blocked until 14:00)`); when every
|
|
22
|
+
* slot is blocked it names the top-ranked slot and marks it blocked instead
|
|
23
|
+
* of silently naming a slot that cannot serve. Block state rides on the
|
|
24
|
+
* stored slots (SlotBlockState); slots without it are treated as usable.
|
|
16
25
|
*/
|
|
17
|
-
export
|
|
26
|
+
export type AccountFooterOptions = {
|
|
27
|
+
/** Effective pin from CLI/settings, outranking the stored pin (senpi#5983). */
|
|
28
|
+
pinnedAccount?: string;
|
|
29
|
+
/** Override for "now" (tests); defaults to `Date.now()`. */
|
|
30
|
+
now?: number;
|
|
31
|
+
};
|
|
32
|
+
export declare function accountFooterSuffix(credential: Credential | undefined, sessionId: string, options?: AccountFooterOptions): string;
|
|
18
33
|
export declare function formatCwdForFooter(cwd: string, home: string | undefined): string;
|
|
19
34
|
/**
|
|
20
35
|
* Footer component that shows pwd, token stats, and context usage.
|
|
@@ -25,7 +40,10 @@ export declare class FooterComponent implements Component {
|
|
|
25
40
|
private footerData;
|
|
26
41
|
private autoCompactEnabled;
|
|
27
42
|
private compactionDelegated;
|
|
28
|
-
|
|
43
|
+
private resolveAccountPin;
|
|
44
|
+
constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider, options?: {
|
|
45
|
+
resolveAccountPin?: (sessionId: string) => string | undefined;
|
|
46
|
+
});
|
|
29
47
|
setSession(session: AgentSession): void;
|
|
30
48
|
setAutoCompactEnabled(enabled: boolean): void;
|
|
31
49
|
/**
|
|
@@ -4,6 +4,7 @@ import { slotsOfOwnKind } from "@earendil-works/pi-ai/auth/pool/kinds";
|
|
|
4
4
|
import { applyAccountOrder, rendezvousOrder } from "@earendil-works/pi-ai/auth/pool/select";
|
|
5
5
|
import { accountLabel } from "@earendil-works/pi-ai/auth/pool/slots";
|
|
6
6
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
7
|
+
import { ANTHROPIC_SUBSCRIPTION_PROVIDER_ID } from "../../../core/extensions/builtin/anthropic-subscription/provider-id.js";
|
|
7
8
|
import { theme } from "../theme/theme.js";
|
|
8
9
|
import { planFooterLayout } from "./footer-layout.js";
|
|
9
10
|
import { buildRightLabel, colorRightSide } from "./footer-right-label.js";
|
|
@@ -39,25 +40,43 @@ export function formatTokens(count) {
|
|
|
39
40
|
return `${trim1(n / 1_000_000_000)}B`;
|
|
40
41
|
return `${Math.round(n / 1_000_000_000)}B`;
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
* engine uses, so the footer names the slot that will serve.
|
|
48
|
-
*/
|
|
49
|
-
export function accountFooterSuffix(credential, sessionId) {
|
|
43
|
+
function formatBlockedUntil(until) {
|
|
44
|
+
const date = new Date(until);
|
|
45
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
46
|
+
}
|
|
47
|
+
export function accountFooterSuffix(credential, sessionId, options = {}) {
|
|
50
48
|
const pooled = credential;
|
|
51
49
|
// Only slots the credential can serve with; a mixed pool's other-kind slot never does (senpi#3486).
|
|
52
50
|
const slots = credential ? slotsOfOwnKind(credential) : [];
|
|
53
51
|
if (slots.length < 2)
|
|
54
52
|
return "";
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
|
|
53
|
+
const now = options.now ?? Date.now();
|
|
54
|
+
// Block state rides on the stored slots at runtime (SlotBlockState) but
|
|
55
|
+
// is not part of the CredentialSlot type, so read it through unknown.
|
|
56
|
+
const blockedUntilOf = (slot) => slot.blockedUntil;
|
|
57
|
+
const isBlocked = (slot) => {
|
|
58
|
+
const until = blockedUntilOf(slot);
|
|
59
|
+
return until !== undefined && until > now;
|
|
60
|
+
};
|
|
61
|
+
const ranked = applyAccountOrder(pooled?.order, rendezvousOrder(sessionId, slots, (input) => createHash("sha256").update(input).digest().readBigUInt64BE(0)));
|
|
62
|
+
const storedPinned = Object.entries(credential ?? {}).find(([key]) => key === "pinned")?.[1];
|
|
63
|
+
const pinnedName = options.pinnedAccount ?? storedPinned;
|
|
64
|
+
const pinnedSlot = pinnedName === undefined ? undefined : slots.find((slot) => slot.name === pinnedName);
|
|
65
|
+
if (pinnedSlot) {
|
|
66
|
+
if (!isBlocked(pinnedSlot))
|
|
67
|
+
return `@${footerAccountLabel(pinnedSlot)}`;
|
|
68
|
+
const until = blockedUntilOf(pinnedSlot);
|
|
69
|
+
const serving = ranked.find((slot) => slot !== pinnedSlot && !isBlocked(slot));
|
|
70
|
+
if (serving)
|
|
71
|
+
return `@${footerAccountLabel(serving)} (${footerAccountLabel(pinnedSlot)} blocked until ${formatBlockedUntil(until)})`;
|
|
72
|
+
return `@${footerAccountLabel(pinnedSlot)} (blocked until ${formatBlockedUntil(until)})`;
|
|
73
|
+
}
|
|
74
|
+
const winner = ranked.find((slot) => !isBlocked(slot)) ?? ranked[0];
|
|
75
|
+
if (winner === undefined)
|
|
76
|
+
return "";
|
|
77
|
+
if (isBlocked(winner))
|
|
78
|
+
return `@${footerAccountLabel(winner)} (blocked until ${formatBlockedUntil(blockedUntilOf(winner))})`;
|
|
79
|
+
return `@${footerAccountLabel(winner)}`;
|
|
61
80
|
}
|
|
62
81
|
/**
|
|
63
82
|
* Widest account label the provider segment carries. A label wider than this
|
|
@@ -90,11 +109,12 @@ export function formatCwdForFooter(cwd, home) {
|
|
|
90
109
|
* Computes token/context stats from session, gets git branch and extension statuses from provider.
|
|
91
110
|
*/
|
|
92
111
|
export class FooterComponent {
|
|
93
|
-
constructor(session, footerData) {
|
|
112
|
+
constructor(session, footerData, options = {}) {
|
|
94
113
|
this.autoCompactEnabled = true;
|
|
95
114
|
this.compactionDelegated = false;
|
|
96
115
|
this.session = session;
|
|
97
116
|
this.footerData = footerData;
|
|
117
|
+
this.resolveAccountPin = options.resolveAccountPin;
|
|
98
118
|
}
|
|
99
119
|
setSession(session) {
|
|
100
120
|
this.session = session;
|
|
@@ -201,7 +221,16 @@ export class FooterComponent {
|
|
|
201
221
|
let accountSuffix = "";
|
|
202
222
|
if (state.model) {
|
|
203
223
|
try {
|
|
204
|
-
|
|
224
|
+
const sessionId = this.session.sessionManager.getSessionId();
|
|
225
|
+
// The resolver carries the Anthropic CLI/settings pin; resolve
|
|
226
|
+
// it only when the current model uses the Anthropic
|
|
227
|
+
// subscription provider. A same-named slot of another pooled
|
|
228
|
+
// provider (e.g. openai-codex "default") keeps its own stored
|
|
229
|
+
// pin instead (senpi#6222 review).
|
|
230
|
+
const pinnedAccount = state.model.provider === ANTHROPIC_SUBSCRIPTION_PROVIDER_ID
|
|
231
|
+
? this.resolveAccountPin?.(sessionId)
|
|
232
|
+
: undefined;
|
|
233
|
+
accountSuffix = accountFooterSuffix(this.session.modelRegistry.authStorage.get(state.model.provider), sessionId, pinnedAccount === undefined ? undefined : { pinnedAccount });
|
|
205
234
|
}
|
|
206
235
|
catch {
|
|
207
236
|
// The footer must render even when credential storage is unreadable.
|
|
@@ -25,6 +25,8 @@ import { collectEntriesForBranchSummary } from "../../core/compaction/branch-sum
|
|
|
25
25
|
import { findExtensionStackMatches } from "../../core/crash-log.js";
|
|
26
26
|
import { AssistantEditError, assistantTextEquals } from "../../core/edited-assistant-message.js";
|
|
27
27
|
import { subscribeProviderAccountEvents } from "../../core/extensions/builtin/anthropic-subscription/account-events.js";
|
|
28
|
+
import { getSessionClaudeAccountPin, resolveClaudeAccountPin, } from "../../core/extensions/builtin/anthropic-subscription/account-command.js";
|
|
29
|
+
import { loadAnthropicSubscriptionProviderSettingsFromDisk } from "../../core/extensions/builtin/anthropic-subscription/settings.js";
|
|
28
30
|
import { formatAccountFailoverNotice } from "./account-failover-notice.js";
|
|
29
31
|
import { askUserAnswerDisplayText, formatUserMessage, showsPartialAnswers, } from "../../core/extensions/builtin/ask-user/format.js";
|
|
30
32
|
import { askUserRenderers } from "../../core/extensions/builtin/ask-user/render.js";
|
|
@@ -817,7 +819,13 @@ export class InteractiveMode {
|
|
|
817
819
|
this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
|
|
818
820
|
this.footer = this.chrome
|
|
819
821
|
? this.chrome.createFooter(this.session, this.footerDataProvider)
|
|
820
|
-
: new FooterComponent(this.session, this.footerDataProvider
|
|
822
|
+
: new FooterComponent(this.session, this.footerDataProvider, {
|
|
823
|
+
// CLI > settings > stored pin, resolved per render so a
|
|
824
|
+
// mid-session /claude-account pin or settings change is
|
|
825
|
+
// picked up (senpi#5983); the stored pin stays the
|
|
826
|
+
// fallback inside accountFooterSuffix.
|
|
827
|
+
resolveAccountPin: (sessionId) => resolveClaudeAccountPin(getSessionClaudeAccountPin(sessionId), loadAnthropicSubscriptionProviderSettingsFromDisk(this.sessionManager.getCwd()).pinnedAccount, undefined),
|
|
828
|
+
});
|
|
821
829
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
822
830
|
this.footerContainer = new Container();
|
|
823
831
|
this.footerContainer.addChild(this.footer);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentSession } from "../../core/agent-session.ts";
|
|
2
2
|
import { type ControlAttachments } from "./session-control-attach.ts";
|
|
3
3
|
import { type ControlCommand, type ControlConnection } from "./session-control-server.ts";
|
|
4
|
-
export declare const TUI_RPC_COMMANDS: readonly ["open_session", "get_snapshot", "subscribe_sessions", "unsubscribe_sessions", "steer", "follow_up", "prompt", "admit_external_message", "get_effect_status", "set_client_info", "attach_session", "release_attachment", "abort", "mcp_presenter_context", "mcp_form_presented", "mcp_form_response", "mcp_form_sync", "mcp_presenter_secret_answer"];
|
|
4
|
+
export declare const TUI_RPC_COMMANDS: readonly ["open_session", "park_session", "claim_session", "get_snapshot", "subscribe_sessions", "unsubscribe_sessions", "steer", "follow_up", "prompt", "admit_external_message", "get_effect_status", "set_client_info", "attach_session", "release_attachment", "abort", "mcp_presenter_context", "mcp_form_presented", "mcp_form_response", "mcp_form_sync", "mcp_presenter_secret_answer"];
|
|
5
5
|
export interface RpcCommandContext {
|
|
6
6
|
/** This endpoint's instance id: the `expectedHostInstance` an `attach_session` may name. */
|
|
7
7
|
readonly instanceId: string;
|
|
@@ -26,6 +26,8 @@ import { failure, success } from "./session-control-server.js";
|
|
|
26
26
|
import { TERMINAL_PRESENTER_COMMANDS } from "./terminal-presenter-surface.js";
|
|
27
27
|
export const TUI_RPC_COMMANDS = [
|
|
28
28
|
"open_session",
|
|
29
|
+
"park_session",
|
|
30
|
+
"claim_session",
|
|
29
31
|
"get_snapshot",
|
|
30
32
|
"subscribe_sessions",
|
|
31
33
|
"unsubscribe_sessions",
|
|
@@ -64,6 +66,9 @@ export async function runTuiRpcCommand(context, connection, command) {
|
|
|
64
66
|
}
|
|
65
67
|
case "open_session":
|
|
66
68
|
return openSession(context, connection, command);
|
|
69
|
+
case "park_session":
|
|
70
|
+
case "claim_session":
|
|
71
|
+
return failure(idOf(command), command.type, "multi_session_disabled");
|
|
67
72
|
case "subscribe_sessions":
|
|
68
73
|
return subscribe(context, connection, frame(command));
|
|
69
74
|
case "unsubscribe_sessions":
|
|
@@ -20,8 +20,8 @@ export interface DeliveryPeer {
|
|
|
20
20
|
/** The remote device the connection speaks for (`ConnectionRoles.originOf`); absent on a plain local connection. */
|
|
21
21
|
readonly origin: RpcRemoteOrigin | undefined;
|
|
22
22
|
readonly connectionOrigin: ConnectionOrigin;
|
|
23
|
-
/** The
|
|
24
|
-
readonly
|
|
23
|
+
/** The origin the turn this delivery opens counts as (senpi#6342); the connection's origin when absent. */
|
|
24
|
+
readonly turnOrigin?: ConnectionOrigin;
|
|
25
25
|
}
|
|
26
26
|
export type AdmitExternalMessageAnswer = {
|
|
27
27
|
readonly ok: true;
|
|
@@ -9,17 +9,9 @@ function ledgerId(deliveryId, origin) {
|
|
|
9
9
|
? `rpc:local/${deliveryId}`
|
|
10
10
|
: `device:${encodeURIComponent(origin.deviceId)}/${deliveryId}`;
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
13
|
-
* The turn a delivery opens: a steer joins the running turn; an agent's delivery on a delegated connection the
|
|
14
|
-
* forwarder granted `peer_agent` is a peer turn (senpi#6305), the record's sender kind being only a consistency check;
|
|
15
|
-
* anything else keeps the connection's origin.
|
|
16
|
-
*/
|
|
12
|
+
/** The turn a delivery opens: a steer joins the running turn; anything else counts as the connection's turn origin. */
|
|
17
13
|
function deliveryTurnMark(command, peer) {
|
|
18
|
-
|
|
19
|
-
return "join";
|
|
20
|
-
if (peer.connectionOrigin === "delegated" && peer.peerAgent === true && command.sender?.kind === "agent")
|
|
21
|
-
return "peer";
|
|
22
|
-
return peer.connectionOrigin;
|
|
14
|
+
return command.deliverAs === "steer" ? "join" : (peer.turnOrigin ?? peer.connectionOrigin);
|
|
23
15
|
}
|
|
24
16
|
export function admitExternalMessage(session, command, peer) {
|
|
25
17
|
const chain = admitInputChain(command.chainCount, session.settingsManager);
|
|
@@ -22,7 +22,10 @@ export function unknownCompactAdmission(host, command) {
|
|
|
22
22
|
if (!host.admissions || command.admissionId === undefined)
|
|
23
23
|
return undefined;
|
|
24
24
|
// These commands introduce client IDs; their bounded attempt index owns retry/conflict/retirement.
|
|
25
|
-
if (command.type === "open_session" ||
|
|
25
|
+
if (command.type === "open_session" ||
|
|
26
|
+
command.type === "claim_session" ||
|
|
27
|
+
command.type === "attach_session" ||
|
|
28
|
+
command.type === "subscribe_sessions")
|
|
26
29
|
return undefined;
|
|
27
30
|
// RULING-96: recovery IDs select token/lease authority, not the ordinary attempt ledger.
|
|
28
31
|
if (command.type === "get_recovery_page" || command.type === "release_recovery")
|
|
@@ -73,6 +73,8 @@ export interface RpcConnectionHandler {
|
|
|
73
73
|
* extension UI context is installed (used by tests that drive ctx.ui directly).
|
|
74
74
|
*/
|
|
75
75
|
readonly ready: Promise<void>;
|
|
76
|
+
/** Complete a prebuilt binding with the capabilities its opener supplied. */
|
|
77
|
+
setClientCapabilities(capabilities: readonly string[]): void;
|
|
76
78
|
/** Feed one inbound JSONL line (command or extension_ui_response). */
|
|
77
79
|
handleInputLine(line: string): Promise<void>;
|
|
78
80
|
/**
|
|
@@ -52,7 +52,8 @@ import { ConnectionQuestionBridge, degradeQuestion, sessionQuestionBridges } fro
|
|
|
52
52
|
import { remoteOriginDeclaration, remoteOriginField } from "./remote-origin.js";
|
|
53
53
|
import { advertisedCompactCapabilities, sessionDeclaration } from "./compact-negotiation.js";
|
|
54
54
|
import { commandAuthority } from "./command-authority.js";
|
|
55
|
-
import {
|
|
55
|
+
import { runInRecordScope } from "../../core/turn-origin.js";
|
|
56
|
+
import { recordTurnOrigin } from "./delegated-drive-origin.js";
|
|
56
57
|
import { ConnectionRoles, DELEGATION_INCARNATION_STALE, roleConnection } from "./connection-roles.js";
|
|
57
58
|
import { ACCOUNT_RESET_CAPABILITY, AUTH_KEY_VERIFY_CAPABILITY, AUTO_TITLE_SESSIONS_CAPABILITY, buildCustomUnsupportedRequest, CONTINUE_FROM_LEAF_CAPABILITY, DEFAULT_CUSTOM_EXTENSION_LABEL, EVAL_CELLS_CAPABILITY, EXTENSION_EVENTS_CAPABILITY, ENTRIES_TAIL_CAPABILITY, INTERACTIVE_USER_CAPABILITY, MEDIA_PLACEHOLDERS_CAPABILITY, PROVIDER_ACCOUNT_TEST_CAPABILITY, QUESTION_CAPABILITY, QUEUED_MESSAGE_REVISION_CAPABILITY, RESUME_SESSION_CAPABILITY, RETRY_FALLBACK_COMMAND_CAPABILITY, SESSION_HELD_CAPABILITY, TOOL_BARRIER_CAPABILITY, TASK_PARENT_CAPABILITY, } from "./custom-capability.js";
|
|
58
59
|
import { runAbort, runGetEffectStatus, runQueuedInputChange, runSetThinkingLevel, } from "./effect-commands.js";
|
|
@@ -209,6 +210,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
209
210
|
const currentConnectionId = () => roleConnection(options.clientInfo?.connectionId());
|
|
210
211
|
const hasRole = (role) => connectionRoles.has(currentConnectionId(), role);
|
|
211
212
|
const connectionOrigin = () => connectionRoles.isDelegated(currentConnectionId()) ? "delegated" : "local";
|
|
213
|
+
// senpi#6342: a turn follows this machine's own setting; only a device the forwarder did not state is the same account
|
|
214
|
+
// counts as delegated for the permission rule.
|
|
215
|
+
const turnOrigin = () => connectionOrigin() === "delegated" && !connectionRoles.sameAccount(currentConnectionId()) ? "delegated" : "local";
|
|
212
216
|
const routingSessionId = options.sessionId;
|
|
213
217
|
// True only while THIS connection's own command drives a replacement; the issuer
|
|
214
218
|
// already learns the new identity from its command response.
|
|
@@ -1009,7 +1013,6 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1009
1013
|
// Handle a single command
|
|
1010
1014
|
const handleCommand = async (command, requestContext) => {
|
|
1011
1015
|
const id = command.id;
|
|
1012
|
-
noteDelegatedRecord(command, connectionOrigin(), session.sessionManager);
|
|
1013
1016
|
switch (command.type) {
|
|
1014
1017
|
case "get_protocol_info":
|
|
1015
1018
|
return {
|
|
@@ -1047,7 +1050,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1047
1050
|
...(options.retryFallbackCommand ? [RETRY_FALLBACK_COMMAND_CAPABILITY] : []),
|
|
1048
1051
|
...(options.toolRuntime ? toolRuntimeCapabilities(options.toolRuntime) : []),
|
|
1049
1052
|
// Client flags cannot advertise a host guard this classic binding does not run, nor a tool runtime.
|
|
1050
|
-
...advertisedCompactCapabilities(options.capabilities ?? [], false).filter((capability) => capability !== SESSION_HELD_CAPABILITY &&
|
|
1053
|
+
...advertisedCompactCapabilities(options.capabilities ?? [], false).filter((capability) => capability !== SESSION_HELD_CAPABILITY &&
|
|
1054
|
+
capability !== "park_claim" &&
|
|
1055
|
+
!isHostOwnedToolRuntimeCapability(capability)),
|
|
1051
1056
|
]),
|
|
1052
1057
|
],
|
|
1053
1058
|
mode: "classic",
|
|
@@ -1055,7 +1060,9 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1055
1060
|
},
|
|
1056
1061
|
};
|
|
1057
1062
|
case "open_session":
|
|
1058
|
-
|
|
1063
|
+
case "park_session":
|
|
1064
|
+
case "claim_session":
|
|
1065
|
+
return error(id, command.type, "multi_session_disabled");
|
|
1059
1066
|
// =================================================================
|
|
1060
1067
|
// Prompting
|
|
1061
1068
|
// =================================================================
|
|
@@ -1066,7 +1073,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1066
1073
|
if (holdAutomaticInput(session, command, hasRole(INTERACTIVE_USER_CAPABILITY)))
|
|
1067
1074
|
return success(id, "prompt", { disposition: "handled", heldAsNotice: true });
|
|
1068
1075
|
turnRequested = true;
|
|
1069
|
-
const admission = handleClientInput(session, command, { output, promptCalls }, { interactiveUser: hasRole(INTERACTIVE_USER_CAPABILITY), connectionOrigin:
|
|
1076
|
+
const admission = handleClientInput(session, command, { output, promptCalls }, { interactiveUser: hasRole(INTERACTIVE_USER_CAPABILITY), connectionOrigin: turnOrigin() });
|
|
1070
1077
|
promptCalls.add(admission);
|
|
1071
1078
|
void admission.finally(() => promptCalls.delete(admission));
|
|
1072
1079
|
return undefined;
|
|
@@ -1136,7 +1143,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1136
1143
|
if (holdAutomaticInput(session, command, hasRole(INTERACTIVE_USER_CAPABILITY)))
|
|
1137
1144
|
return success(id, command.type, { disposition: "handled", heldAsNotice: true });
|
|
1138
1145
|
turnRequested = true;
|
|
1139
|
-
await handleClientInput(session, command, { output, promptCalls }, { interactiveUser: hasRole(INTERACTIVE_USER_CAPABILITY), connectionOrigin:
|
|
1146
|
+
await handleClientInput(session, command, { output, promptCalls }, { interactiveUser: hasRole(INTERACTIVE_USER_CAPABILITY), connectionOrigin: turnOrigin() });
|
|
1140
1147
|
return undefined;
|
|
1141
1148
|
}
|
|
1142
1149
|
case "abort":
|
|
@@ -1238,7 +1245,7 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1238
1245
|
const peer = {
|
|
1239
1246
|
origin: connectionRoles.originOf(currentConnectionId()),
|
|
1240
1247
|
connectionOrigin: connectionOrigin(),
|
|
1241
|
-
|
|
1248
|
+
turnOrigin: turnOrigin(),
|
|
1242
1249
|
};
|
|
1243
1250
|
const answer = admitExternalMessage(session, command, peer);
|
|
1244
1251
|
if (!answer.ok) {
|
|
@@ -1991,7 +1998,8 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
1991
1998
|
return;
|
|
1992
1999
|
}
|
|
1993
2000
|
try {
|
|
1994
|
-
|
|
2001
|
+
// senpi#6342: the record's own handling, and the first run it starts, run under the record's origin.
|
|
2002
|
+
const response = await runInRecordScope(recordTurnOrigin(command, turnOrigin()), () => handleCommand(command, requestContext));
|
|
1995
2003
|
if (response) {
|
|
1996
2004
|
output(response);
|
|
1997
2005
|
await waitForRpcBackpressure();
|
|
@@ -2052,6 +2060,10 @@ export function createRpcConnectionHandler(runtimeHost, sink, options = {}) {
|
|
|
2052
2060
|
pendingPrompts() {
|
|
2053
2061
|
return [...promptCalls];
|
|
2054
2062
|
},
|
|
2063
|
+
setClientCapabilities(capabilities) {
|
|
2064
|
+
replaceClientCapabilities([...capabilities]);
|
|
2065
|
+
refreshGrantPresentation();
|
|
2066
|
+
},
|
|
2055
2067
|
isShutdownRequested() {
|
|
2056
2068
|
return shutdownRequested;
|
|
2057
2069
|
},
|
|
@@ -59,6 +59,8 @@ export declare class ConnectionRoles {
|
|
|
59
59
|
acceptsOrigin(connectionId: string, origin: RpcRemoteOrigin): boolean;
|
|
60
60
|
/** Records the remote device a local connection speaks for; a delegated connection's origin is never declared. */
|
|
61
61
|
declareOrigin(connectionId: string, origin: RpcRemoteOrigin): void;
|
|
62
|
+
/** Whether the forwarder stated this delegated connection's device is the host's own account (senpi#6342). */
|
|
63
|
+
sameAccount(connectionId: string | undefined): boolean;
|
|
62
64
|
/** The remote device this connection speaks for: a delegated connection's principal, else its declared origin. */
|
|
63
65
|
originOf(connectionId: string | undefined): RpcRemoteOrigin | undefined;
|
|
64
66
|
/** Whether the connection is delegated (a remote device's): on the host, retained by a worker, or carried to one. */
|
|
@@ -117,6 +117,10 @@ export class ConnectionRoles {
|
|
|
117
117
|
if (this.acceptsOrigin(connectionId, origin))
|
|
118
118
|
this.origins.set(connectionId, origin);
|
|
119
119
|
}
|
|
120
|
+
/** Whether the forwarder stated this delegated connection's device is the host's own account (senpi#6342). */
|
|
121
|
+
sameAccount(connectionId) {
|
|
122
|
+
return this.viewOf(roleConnection(connectionId))?.descriptor.principal.sameAccount === true;
|
|
123
|
+
}
|
|
120
124
|
/** The remote device this connection speaks for: a delegated connection's principal, else its declared origin. */
|
|
121
125
|
originOf(connectionId) {
|
|
122
126
|
const key = roleConnection(connectionId);
|
|
@@ -65,6 +65,8 @@ export declare const DELEGATED_CONNECTION_CAPABILITY = "delegated_connection_v1"
|
|
|
65
65
|
export declare const DELEGATION_ROLES_CAPABILITY = "delegation_roles_v1";
|
|
66
66
|
/** A host-mode delegation may carry the forwarder-granted `peer_agent` role (senpi#6305). */
|
|
67
67
|
export declare const PEER_AGENT_ROLE_CAPABILITY = "peer_agent_role_v1";
|
|
68
|
+
/** A delegation may state `principal.sameAccount`, so that device's turns follow this host's own setting (senpi#6342). */
|
|
69
|
+
export declare const SAME_ACCOUNT_DELEGATION_CAPABILITY = "same_account_delegation_v1";
|
|
68
70
|
/**
|
|
69
71
|
* Client opt-in (D11 S3-5): the connection renders the value-free `secret_input_notice` and `grant_select_notice` of a
|
|
70
72
|
* raiser-only dialog another connection presents, and their `*_notice_settled` records. It never answers them.
|
|
@@ -117,6 +119,8 @@ export declare const AUTO_TITLE_PER_SESSION_CAPABILITY = "auto_title_per_session
|
|
|
117
119
|
* cwd, kind and context needs without opening a session. Advertised only by an in-process runtime.
|
|
118
120
|
*/
|
|
119
121
|
export declare const WARM_CAPABILITY = "warm";
|
|
122
|
+
/** HOST capability: invisible speculative sessions and single-use claim, in-process registries only. */
|
|
123
|
+
export declare const PARK_CLAIM_CAPABILITY = "park_claim";
|
|
120
124
|
/**
|
|
121
125
|
* HOST capability: this host honors `open_session.durableSessionId`, so a caller that already
|
|
122
126
|
* owns a stable record id for the conversation can CREATE the session under that id and keep
|
|
@@ -65,6 +65,8 @@ export const DELEGATED_CONNECTION_CAPABILITY = "delegated_connection_v1";
|
|
|
65
65
|
export const DELEGATION_ROLES_CAPABILITY = "delegation_roles_v1";
|
|
66
66
|
/** A host-mode delegation may carry the forwarder-granted `peer_agent` role (senpi#6305). */
|
|
67
67
|
export const PEER_AGENT_ROLE_CAPABILITY = "peer_agent_role_v1";
|
|
68
|
+
/** A delegation may state `principal.sameAccount`, so that device's turns follow this host's own setting (senpi#6342). */
|
|
69
|
+
export const SAME_ACCOUNT_DELEGATION_CAPABILITY = "same_account_delegation_v1";
|
|
68
70
|
/**
|
|
69
71
|
* Client opt-in (D11 S3-5): the connection renders the value-free `secret_input_notice` and `grant_select_notice` of a
|
|
70
72
|
* raiser-only dialog another connection presents, and their `*_notice_settled` records. It never answers them.
|
|
@@ -117,6 +119,8 @@ export const AUTO_TITLE_PER_SESSION_CAPABILITY = "auto_title_per_session";
|
|
|
117
119
|
* cwd, kind and context needs without opening a session. Advertised only by an in-process runtime.
|
|
118
120
|
*/
|
|
119
121
|
export const WARM_CAPABILITY = "warm";
|
|
122
|
+
/** HOST capability: invisible speculative sessions and single-use claim, in-process registries only. */
|
|
123
|
+
export const PARK_CLAIM_CAPABILITY = "park_claim";
|
|
120
124
|
/**
|
|
121
125
|
* HOST capability: this host honors `open_session.durableSessionId`, so a caller that already
|
|
122
126
|
* owns a stable record id for the conversation can CREATE the session under that id and keep
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type ConnectionOrigin } from "../../core/turn-origin.ts";
|
|
1
|
+
import type { ConnectionOrigin } from "../../core/turn-origin.ts";
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
* by the engine's own method-class table (`drive`, and `device_admin`, which grants more),
|
|
6
|
-
* commands, so a new run starter (`continue_from_leaf`, `wake`, `resume_for_run`, ...)
|
|
3
|
+
* The origin a record's own handling runs under (senpi#6342): delegated for a cross-account record that can act on the
|
|
4
|
+
* session's work, decided by the engine's own method-class table (`drive`, and `device_admin`, which grants more),
|
|
5
|
+
* never by a hand-kept list of commands, so a new run starter (`continue_from_leaf`, `wake`, `resume_for_run`, ...)
|
|
6
|
+
* cannot slip past the rule. Any other record runs as local.
|
|
7
7
|
*/
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function recordTurnOrigin(command: object, origin: ConnectionOrigin): ConnectionOrigin;
|
|
9
9
|
//# sourceMappingURL=delegated-drive-origin.d.ts.map
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { recordDelegatedOrigin } from "../../core/turn-origin.js";
|
|
2
1
|
import { classifyRpcMethod } from "./method-classes.js";
|
|
3
2
|
const ACTING_CLASSES = new Set(["drive", "device_admin"]);
|
|
4
3
|
/**
|
|
5
|
-
*
|
|
6
|
-
* by the engine's own method-class table (`drive`, and `device_admin`, which grants more),
|
|
7
|
-
* commands, so a new run starter (`continue_from_leaf`, `wake`, `resume_for_run`, ...)
|
|
4
|
+
* The origin a record's own handling runs under (senpi#6342): delegated for a cross-account record that can act on the
|
|
5
|
+
* session's work, decided by the engine's own method-class table (`drive`, and `device_admin`, which grants more),
|
|
6
|
+
* never by a hand-kept list of commands, so a new run starter (`continue_from_leaf`, `wake`, `resume_for_run`, ...)
|
|
7
|
+
* cannot slip past the rule. Any other record runs as local.
|
|
8
8
|
*/
|
|
9
|
-
export function
|
|
9
|
+
export function recordTurnOrigin(command, origin) {
|
|
10
10
|
if (origin !== "delegated")
|
|
11
|
-
return;
|
|
11
|
+
return "local";
|
|
12
12
|
const methodClass = classifyRpcMethod(command);
|
|
13
|
-
|
|
14
|
-
recordDelegatedOrigin(sessionManager);
|
|
13
|
+
return methodClass !== undefined && ACTING_CLASSES.has(methodClass) ? "delegated" : "local";
|
|
15
14
|
}
|
|
16
15
|
//# sourceMappingURL=delegated-drive-origin.js.map
|
|
@@ -4,6 +4,8 @@ export interface Principal {
|
|
|
4
4
|
readonly deviceId: string;
|
|
5
5
|
readonly deviceName?: string;
|
|
6
6
|
readonly conn?: string;
|
|
7
|
+
/** The forwarder states this device is the host's own account (senpi#6342); fixed for the record's life. */
|
|
8
|
+
readonly sameAccount?: true;
|
|
7
9
|
}
|
|
8
10
|
export interface Declaration {
|
|
9
11
|
readonly principal: Principal;
|
|
@@ -22,6 +22,9 @@ export function parseDeclaration(record) {
|
|
|
22
22
|
return "principal.deviceName must be 1-256 characters";
|
|
23
23
|
if (conn !== undefined && !boundedString(conn, MAX_CONN_FIELD))
|
|
24
24
|
return "principal.conn must be 1-64 characters";
|
|
25
|
+
const sameAccount = Reflect.get(principal, "sameAccount");
|
|
26
|
+
if (sameAccount !== undefined && sameAccount !== true)
|
|
27
|
+
return "principal.sameAccount must be true when present";
|
|
25
28
|
const granted = Reflect.get(record, "granted");
|
|
26
29
|
if (!Array.isArray(granted))
|
|
27
30
|
return "granted must be an array of method classes";
|
|
@@ -40,12 +43,13 @@ export function parseDeclaration(record) {
|
|
|
40
43
|
deviceId: deviceId,
|
|
41
44
|
...(deviceName === undefined ? {} : { deviceName: deviceName }),
|
|
42
45
|
...(conn === undefined ? {} : { conn: conn }),
|
|
46
|
+
...(sameAccount === true ? { sameAccount: true } : {}),
|
|
43
47
|
},
|
|
44
48
|
granted: classes,
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
51
|
export function samePrincipal(a, b) {
|
|
48
|
-
return a.deviceId === b.deviceId && a.deviceName === b.deviceName && a.conn === b.conn;
|
|
52
|
+
return (a.deviceId === b.deviceId && a.deviceName === b.deviceName && a.conn === b.conn && a.sameAccount === b.sameAccount);
|
|
49
53
|
}
|
|
50
54
|
function parseRoles(value) {
|
|
51
55
|
if (value === undefined)
|
|
@@ -66,6 +66,27 @@ export declare function runSetThinkingLevel(ctx: EffectCommandContext, command:
|
|
|
66
66
|
readonly reclaimed: true;
|
|
67
67
|
readonly sessionId: string;
|
|
68
68
|
};
|
|
69
|
+
} | {
|
|
70
|
+
id?: string;
|
|
71
|
+
type: "response";
|
|
72
|
+
command: "park_session";
|
|
73
|
+
success: true;
|
|
74
|
+
data: {
|
|
75
|
+
state: "parked" | "already_parked" | "full";
|
|
76
|
+
};
|
|
77
|
+
} | {
|
|
78
|
+
id?: string;
|
|
79
|
+
type: "response";
|
|
80
|
+
command: "claim_session";
|
|
81
|
+
success: true;
|
|
82
|
+
data: {
|
|
83
|
+
claimed: false;
|
|
84
|
+
} | {
|
|
85
|
+
sessionId: string;
|
|
86
|
+
state: import("./rpc-session-state-types.ts").RpcSessionState;
|
|
87
|
+
attached?: boolean;
|
|
88
|
+
presence?: import("./rpc-host-response-types.ts").RpcSessionPresence;
|
|
89
|
+
};
|
|
69
90
|
} | {
|
|
70
91
|
id?: string;
|
|
71
92
|
type: "response";
|
|
@@ -14,6 +14,7 @@ export declare const SET_PERMISSION_PRESET_CAPABILITY = "set_permission_preset_v
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function multiSessionHostCapabilities(options: {
|
|
16
16
|
readonly warm: boolean;
|
|
17
|
+
readonly parkClaim?: boolean;
|
|
17
18
|
readonly toolRuntime: ToolRuntimeKind;
|
|
18
19
|
readonly negotiated: readonly string[];
|
|
19
20
|
readonly mcpPresenters?: boolean;
|
|
@@ -2,7 +2,7 @@ import { compactUpdatesEnabled } from "../../core/engine-socket-flag.js";
|
|
|
2
2
|
import { isHostOwnedToolRuntimeCapability, toolRuntimeCapabilities, } from "../../cli/tool-runtime.js";
|
|
3
3
|
import { ACCEPT_EDITS_PERMISSION_PRESET_CAPABILITY, AUTO_PERMISSION_PRESET_CAPABILITY, } from "../../core/extensions/builtin/permission-system/config.js";
|
|
4
4
|
import { DURABLE_CLIENT_MESSAGE_ID_CAPABILITY } from "./client-admission-record.js";
|
|
5
|
-
import { ACCOUNT_RESET_CAPABILITY, ATTENDED_CAPABILITY, AUTH_KEY_VERIFY_CAPABILITY, AUTO_TITLE_PER_SESSION_CAPABILITY, AUTO_TITLE_SESSIONS_CAPABILITY, BROWSER_ENGINE_CAPABILITY, CONTINUE_FROM_LEAF_CAPABILITY, DELEGATED_CONNECTION_CAPABILITY, DELEGATION_ROLES_CAPABILITY, DETACH_CONNECTION_CAPABILITY, DURABLE_SESSION_ID_CAPABILITY, PEER_AGENT_ROLE_CAPABILITY, ENTRIES_TAIL_CAPABILITY, EVAL_CELLS_CAPABILITY, MEDIA_PLACEHOLDERS_CAPABILITY, MOVED_PATH_GUARD_CAPABILITY, OBSERVE_READS_CAPABILITY, PROMPT_SURFACE_CAPABILITY, PROMPT_SURFACE_CHAT_CAPABILITY, PROVIDER_ACCOUNT_TEST_CAPABILITY, QUEUED_MESSAGE_REVISION_CAPABILITY, RESUME_SESSION_CAPABILITY, RETAIN_ON_DISCONNECT_CAPABILITY, RETRY_FALLBACK_PROFILE_CAPABILITY, SESSION_CONTEXT_CAPABILITY, SESSION_ENV_CAPABILITY, SESSION_HELD_CAPABILITY, SESSION_KIND_CAPABILITY, SESSION_RELOCATION_CAPABILITY, SESSION_STATUS_CAPABILITY, TOOL_BARRIER_CAPABILITY, WARM_CAPABILITY, } from "./custom-capability.js";
|
|
5
|
+
import { ACCOUNT_RESET_CAPABILITY, ATTENDED_CAPABILITY, AUTH_KEY_VERIFY_CAPABILITY, AUTO_TITLE_PER_SESSION_CAPABILITY, AUTO_TITLE_SESSIONS_CAPABILITY, BROWSER_ENGINE_CAPABILITY, CONTINUE_FROM_LEAF_CAPABILITY, DELEGATED_CONNECTION_CAPABILITY, DELEGATION_ROLES_CAPABILITY, DETACH_CONNECTION_CAPABILITY, DURABLE_SESSION_ID_CAPABILITY, PEER_AGENT_ROLE_CAPABILITY, SAME_ACCOUNT_DELEGATION_CAPABILITY, ENTRIES_TAIL_CAPABILITY, EVAL_CELLS_CAPABILITY, MEDIA_PLACEHOLDERS_CAPABILITY, MOVED_PATH_GUARD_CAPABILITY, OBSERVE_READS_CAPABILITY, PROMPT_SURFACE_CAPABILITY, PROMPT_SURFACE_CHAT_CAPABILITY, PROVIDER_ACCOUNT_TEST_CAPABILITY, QUEUED_MESSAGE_REVISION_CAPABILITY, RESUME_SESSION_CAPABILITY, RETAIN_ON_DISCONNECT_CAPABILITY, RETRY_FALLBACK_PROFILE_CAPABILITY, SESSION_CONTEXT_CAPABILITY, SESSION_ENV_CAPABILITY, SESSION_HELD_CAPABILITY, SESSION_KIND_CAPABILITY, SESSION_RELOCATION_CAPABILITY, SESSION_STATUS_CAPABILITY, TOOL_BARRIER_CAPABILITY, WARM_CAPABILITY, PARK_CLAIM_CAPABILITY, } from "./custom-capability.js";
|
|
6
6
|
import { EFFECT_LEDGER_CAPABILITY } from "./effect-record.js";
|
|
7
7
|
import { SESSION_MODEL_SCOPE_CAPABILITY } from "./rpc-session-command-types.js";
|
|
8
8
|
import { STEER_WHEN_IDLE_CAPABILITY, USER_ACTIVITY_CAPABILITY } from "./rpc-types.js";
|
|
@@ -72,6 +72,8 @@ export function multiSessionHostCapabilities(options) {
|
|
|
72
72
|
DELEGATION_ROLES_CAPABILITY,
|
|
73
73
|
// ...and may carry the forwarder-granted `peer_agent` role on such a connection (senpi#6305).
|
|
74
74
|
PEER_AGENT_ROLE_CAPABILITY,
|
|
75
|
+
// ...and lets the forwarder state a device is the host's own account, whose turns then ask nothing extra.
|
|
76
|
+
SAME_ACCOUNT_DELEGATION_CAPABILITY,
|
|
75
77
|
// Only a multi-session host keeps sessions loaded that a client can ask it to free.
|
|
76
78
|
UNLOAD_SESSION_CAPABILITY,
|
|
77
79
|
// Only a multi-session host owns a move ledger and the hosted-session list export checks against.
|
|
@@ -103,9 +105,11 @@ export function multiSessionHostCapabilities(options) {
|
|
|
103
105
|
AUTO_PERMISSION_PRESET_CAPABILITY,
|
|
104
106
|
// Only an in-process runtime shares the loop a warm loads into (senpi#2314).
|
|
105
107
|
...(options.warm ? [WARM_CAPABILITY] : []),
|
|
108
|
+
...(options.parkClaim ? [PARK_CLAIM_CAPABILITY] : []),
|
|
106
109
|
...toolRuntimeCapabilities(options.toolRuntime),
|
|
107
110
|
// The host alone says where tool work runs; a negotiated copy would make two answers.
|
|
108
111
|
...options.negotiated.filter((capability) => capability !== "mcp_form_presenter_v1" &&
|
|
112
|
+
capability !== PARK_CLAIM_CAPABILITY &&
|
|
109
113
|
// RULING-111 (LEAK-E): only the compact negotiation owns these names; flag off echoes them as main does.
|
|
110
114
|
(!compactUpdatesEnabled() ||
|
|
111
115
|
(capability !== "compact_message_updates_v1" && capability !== "compact_events")) &&
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* and the connection attaches, so a client cannot hold a session while its host idles out under it.
|
|
13
13
|
* `warm` never attaches, marked or not: it opens nothing, so a host that only received it idles out
|
|
14
14
|
* on its normal deadline (senpi#2314).
|
|
15
|
+
* `park_session` likewise holds no client attachment: its speculative runtime is discarded at host exit.
|
|
16
|
+
* `claim_session` takes an ordinary opener attachment and is never observing.
|
|
15
17
|
* Hosts that predate the field ignore it (commands are parsed by `type`), so a marked read works
|
|
16
18
|
* against every generation.
|
|
17
19
|
*
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* and the connection attaches, so a client cannot hold a session while its host idles out under it.
|
|
13
13
|
* `warm` never attaches, marked or not: it opens nothing, so a host that only received it idles out
|
|
14
14
|
* on its normal deadline (senpi#2314).
|
|
15
|
+
* `park_session` likewise holds no client attachment: its speculative runtime is discarded at host exit.
|
|
16
|
+
* `claim_session` takes an ordinary opener attachment and is never observing.
|
|
15
17
|
* Hosts that predate the field ignore it (commands are parsed by `type`), so a marked read works
|
|
16
18
|
* against every generation.
|
|
17
19
|
*
|
|
@@ -59,7 +61,10 @@ export function isObservingRequest(line) {
|
|
|
59
61
|
if (typeof request !== "object" || request === null || Array.isArray(request))
|
|
60
62
|
return false;
|
|
61
63
|
const record = request;
|
|
62
|
-
if (record.type === "warm" ||
|
|
64
|
+
if (record.type === "warm" ||
|
|
65
|
+
record.type === "park_session" ||
|
|
66
|
+
record.type === "authenticate" ||
|
|
67
|
+
record.type === RPC_LAUNCH_PROOF_RECORD)
|
|
63
68
|
return true;
|
|
64
69
|
return record[OBSERVE_REQUEST_FIELD] === true && OBSERVING_COMMANDS.has(record.type);
|
|
65
70
|
}
|