@code-yeongyu/senpi 2026.11.10 → 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 +95 -0
- package/dist/bundle/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
- package/dist/bundle/chunks/{SKILL-AZ6ONO52.js → SKILL-5O2UKORG.js} +1 -1
- package/dist/bundle/chunks/{SKILL-B4R7BVOT.md → SKILL-R7FEIQKG.md} +1 -1
- 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-3IYEGHBI.js +2 -0
- package/dist/bundle/chunks/{chunk-I5ASHSLZ.js → chunk-4JMGWVRH.js} +1 -1
- package/dist/bundle/chunks/{chunk-YKYWL2NO.js → chunk-5F2XC6UC.js} +3 -3
- package/dist/bundle/chunks/{chunk-MSDBBOB2.js → chunk-5H43O4MA.js} +1 -1
- package/dist/bundle/chunks/{chunk-R3OP7IA4.js → chunk-5PIZWSNG.js} +4 -4
- package/dist/bundle/chunks/{chunk-33MZJTQ3.js → chunk-B4WD5XHP.js} +1 -1
- package/dist/bundle/chunks/{chunk-Q63YLZMN.js → chunk-BPEA5SWW.js} +2 -2
- package/dist/bundle/chunks/{chunk-MOVJQVMC.js → chunk-BUDJLVMF.js} +3 -3
- package/dist/bundle/chunks/{chunk-V5FSENW6.js → chunk-COKX65TE.js} +1 -1
- package/dist/bundle/chunks/{chunk-2EJ4BGBM.js → chunk-DHORIIVQ.js} +1 -1
- 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-GVPZPVVO.js → chunk-IBDAV2AL.js} +3 -3
- package/dist/bundle/chunks/chunk-IREUIMFE.js +11 -0
- package/dist/bundle/chunks/{chunk-KU7OCWJF.js → chunk-J4ADZ2OK.js} +1 -1
- package/dist/bundle/chunks/{chunk-T2SMQPRE.js → chunk-K3GAGIPX.js} +1 -1
- package/dist/bundle/chunks/{chunk-BELNOPAI.js → chunk-L3KVP7VJ.js} +3 -3
- package/dist/bundle/chunks/{chunk-IYSJFQUU.js → chunk-L6DF5G6K.js} +1 -1
- package/dist/bundle/chunks/{chunk-7MN7YNO5.js → chunk-L7DGTA3L.js} +1 -1
- package/dist/bundle/chunks/chunk-LJFQYCFH.js +2 -0
- 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-2U6HXWXS.js → chunk-NNQ34ZDW.js} +1 -1
- package/dist/bundle/chunks/chunk-NQKCJQHB.js +2 -0
- package/dist/bundle/chunks/{chunk-EJB5E2JK.js → chunk-NRZAJUUO.js} +2 -2
- package/dist/bundle/chunks/{chunk-R2Z7CX77.js → chunk-OSNR3IRD.js} +1 -1
- package/dist/bundle/chunks/{chunk-O3FG6HML.js → chunk-PJI5GT7U.js} +1 -1
- package/dist/bundle/chunks/{chunk-FDNM2DAE.js → chunk-PUZDKFRT.js} +164 -164
- 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-2BH54YQR.js → chunk-SANWO37B.js} +1 -1
- package/dist/bundle/chunks/{chunk-YQZNARTX.js → chunk-SS5VTBAV.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-FAFIGBUD.js → chunk-XUDKT4UH.js} +3 -3
- package/dist/bundle/chunks/{cli-main-BX5AFWJQ.js → cli-main-7II7GCIR.js} +1 -1
- package/dist/bundle/chunks/{credential-accounts-LVVPKFEY.js → credential-accounts-N3B5YJS7.js} +1 -1
- package/dist/bundle/chunks/{dispatch-EP35KKJ3.js → dispatch-YUYZQXRP.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{hand-entry-F6PWKNPJ.js → hand-entry-ONOLRMVB.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-47QQKJEY.js → host-command-KWMJLPBS.js} +1 -1
- package/dist/bundle/chunks/{host-lifecycle-VSAH4M3R.js → host-lifecycle-4WQDCN3Y.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle.js +5 -5
- package/dist/bundle/chunks/{host-session-command-SL4AN5WD.js → host-session-command-ZDYEKCUB.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-JDMIHJMQ.js → interactive-mode-EPE4M32G.js} +1 -1
- package/dist/bundle/chunks/{login-health-EVXVLHB2.js → login-health-TOSQT5RG.js} +1 -1
- package/dist/bundle/chunks/{main-4RITUAQF.js → main-OM7BKVZG.js} +1 -1
- package/dist/bundle/chunks/{multi-session-host-SKDD4OWM.js → multi-session-host-DB6XMY53.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-B5VA4KFO.js → package-manager-cli-GLU5Q2KV.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-SDJVBT36.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-FPEQCX33.js → schedule-command-5SQD7M5N.js} +1 -1
- package/dist/bundle/chunks/session-control-endpoint-R25A2QGS.js +3 -0
- package/dist/bundle/chunks/{session-picker-2FZSNFS7.js → session-picker-GZM5CVQL.js} +1 -1
- package/dist/bundle/chunks/{session-slot-CZGQQCOC.js → session-slot-RH7JLO3F.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +335 -332
- 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 +5 -5
- 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/cli.js +9 -3
- 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 +15 -3
- package/dist/core/agent-session.js +91 -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/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/egress/wire.d.ts +13 -0
- package/dist/core/extensions/builtin/anthropic-subscription/accounts.d.ts +3 -0
- package/dist/core/extensions/builtin/anthropic-subscription/auth-lane.js +3 -2
- 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/claude-settings-env.d.ts +20 -0
- package/dist/core/extensions/builtin/anthropic-subscription/claude-settings-env.js +39 -0
- package/dist/core/extensions/builtin/anthropic-subscription/context-window.d.ts +17 -0
- package/dist/core/extensions/builtin/anthropic-subscription/context-window.js +23 -0
- 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/anthropic-subscription/prompt-cache-ttl.d.ts +10 -4
- package/dist/core/extensions/builtin/anthropic-subscription/prompt-cache-ttl.js +14 -5
- package/dist/core/extensions/builtin/anthropic-subscription/query-environment.d.ts +3 -2
- package/dist/core/extensions/builtin/anthropic-subscription/query-environment.js +5 -4
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.d.ts +2 -1
- package/dist/core/extensions/builtin/anthropic-subscription/sdk-boundary.lazy.js +1 -0
- package/dist/core/extensions/builtin/anthropic-subscription/stream.js +11 -1
- package/dist/core/extensions/builtin/config-reload/change-groups.js +6 -2
- 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 +14 -26
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +8 -6
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +77 -39
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +66 -7
- package/dist/core/extensions/builtin/goal/persistence.js +20 -0
- 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 +3 -2
- package/dist/core/extensions/builtin/goal/store.js +9 -2
- package/dist/core/extensions/builtin/goal/types.d.ts +9 -0
- 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/html-render/tool.js +13 -3
- package/dist/core/extensions/builtin/imagegen/skill/SKILL.md +1 -1
- package/dist/core/extensions/builtin/imagegen/tool.js +4 -14
- package/dist/core/extensions/builtin/mcp/auth/catalog-identity.js +32 -8
- package/dist/core/extensions/builtin/mcp/auth/commands-auth.js +2 -1
- package/dist/core/extensions/builtin/mcp/auth/context.js +3 -0
- 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-issuer.d.ts +7 -1
- package/dist/core/extensions/builtin/mcp/auth/oauth-issuer.js +13 -0
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.d.ts +6 -17
- package/dist/core/extensions/builtin/mcp/auth/oauth-provider.js +67 -88
- 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/registered-redirect.d.ts +19 -0
- package/dist/core/extensions/builtin/mcp/auth/registered-redirect.js +55 -0
- package/dist/core/extensions/builtin/mcp/auth/token-store.d.ts +23 -1
- 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/permission-system/parsers.js +1 -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 +9 -8
- 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/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 +19 -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 +21 -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/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/retry-fallback/settings.d.ts +8 -5
- package/dist/core/retry-fallback/settings.js +9 -6
- 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 +18 -10
- 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 -0
- package/dist/modes/rpc/admit-external-message-command.js +5 -1
- package/dist/modes/rpc/compact-admission-validation.js +4 -1
- package/dist/modes/rpc/connection-authority.d.ts +2 -1
- package/dist/modes/rpc/connection-authority.js +11 -9
- package/dist/modes/rpc/connection-handler.d.ts +2 -0
- package/dist/modes/rpc/connection-handler.js +33 -7
- package/dist/modes/rpc/connection-roles.d.ts +7 -0
- package/dist/modes/rpc/connection-roles.js +15 -0
- package/dist/modes/rpc/custom-capability.d.ts +6 -0
- package/dist/modes/rpc/custom-capability.js +6 -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 +7 -3
- package/dist/modes/rpc/effect-commands.d.ts +33 -0
- package/dist/modes/rpc/host-capabilities.d.ts +1 -0
- package/dist/modes/rpc/host-capabilities.js +7 -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/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-session.d.ts +20 -0
- package/dist/modes/rpc/method-classes-schema-session.js +6 -0
- package/dist/modes/rpc/method-classes-schema.d.ts +62 -1
- package/dist/modes/rpc/method-classes-schema.js +5 -2
- package/dist/modes/rpc/method-classes.d.ts +4 -0
- package/dist/modes/rpc/method-classes.js +6 -0
- package/dist/modes/rpc/multi-session-host.js +1 -1
- package/dist/modes/rpc/project-file-commands.d.ts +32 -0
- package/dist/modes/rpc/project-file-commands.js +195 -0
- package/dist/modes/rpc/project-file-path.d.ts +34 -0
- package/dist/modes/rpc/project-file-path.js +93 -0
- package/dist/modes/rpc/rpc-error-types.d.ts +9 -1
- package/dist/modes/rpc/rpc-error-types.js +8 -0
- 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/rpc-mutation-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-read-response-types.d.ts +7 -0
- package/dist/modes/rpc/rpc-session-command-types.d.ts +10 -0
- package/dist/modes/rpc/rpc-session-state-types.d.ts +2 -0
- package/dist/modes/rpc/session-attachment-gate.js +6 -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-command-activity.js +1 -0
- 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/dist/private-fd-startup-fillers.d.ts +2 -2
- package/dist/private-fd-startup-fillers.js +34 -4
- package/dist/senpi +9 -3
- package/docs/engine-socket/fixtures/admission-routes/h-legacy.json +1 -1
- package/docs/engine-socket/fixtures/rpc-inbound-samples.json +44 -2
- package/docs/extensions.md +11 -1
- package/docs/rpc.md +106 -2
- package/docs/settings.md +5 -3
- package/docs/skills.md +1 -1
- 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-SJI6UB23.js +0 -2
- package/dist/bundle/chunks/chunk-XH2MWTXA.js +0 -11
- package/dist/bundle/chunks/chunk-YK3ZZCHI.js +0 -2
- package/dist/bundle/chunks/openai-codex-responses-ABB2J43U.js +0 -10
- package/dist/bundle/chunks/rpc-mode-ZDUGW2IM.js +0 -2
- package/dist/bundle/chunks/session-control-endpoint-MEDV66PS.js +0 -3
|
@@ -6,5 +6,11 @@ export interface FallbackLogger {
|
|
|
6
6
|
export interface FallbackLoggerOptions {
|
|
7
7
|
maxBytes?: number;
|
|
8
8
|
}
|
|
9
|
-
export
|
|
9
|
+
export interface SettledFallbackLogger extends FallbackLogger {
|
|
10
|
+
/** Resolves once the one-time removal of earlier log generations has finished and every line logged before it was written. */
|
|
11
|
+
settled(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
/** Resolves once every fallback logger created so far has finished its first-start removal and flushed its queue. */
|
|
14
|
+
export declare function settledFallbackLoggers(): Promise<void>;
|
|
15
|
+
export declare function createFallbackLogger(agentDir: string, options?: FallbackLoggerOptions): SettledFallbackLogger;
|
|
10
16
|
//# sourceMappingURL=log.d.ts.map
|
|
@@ -1,19 +1,81 @@
|
|
|
1
1
|
import { closeSync, fchmodSync, mkdirSync, openSync, writeSync } from "node:fs";
|
|
2
|
+
import { access, mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
3
|
import { dirname, join } from "node:path";
|
|
3
4
|
import { rotateLogIfNeeded } from "../log-file-rotation.js";
|
|
4
|
-
import { redactSecrets } from "../sensitive-output.js";
|
|
5
|
+
import { redactSecrets, redactSensitiveTokenValues } from "../sensitive-output.js";
|
|
5
6
|
const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
6
7
|
const MAX_STRING_LENGTH = 200;
|
|
8
|
+
const MAX_QUEUED_LINES = 256;
|
|
7
9
|
const BLOCKED_KEY = /^(?:__proto__|constructor|prototype|headers?|env(?:ironment)?|authorization|credential(?:s)?|password|secret|token|api_?key|client_?secret)$/i;
|
|
8
10
|
const ALLOWED_DATA_KEY = /^(?:selector|candidate|currentSelector|originalSelector|from|to|model|provider|chainKey|reason|skipReason|classification|thinkingLevel|durationMs|retryAfterMs|error|errorMessage|warning|trigger)$/;
|
|
9
11
|
const SENSITIVE_TEXT = /((?:authorization\s*[:=]\s*(?:bearer|basic)\s+)|(?:bearer\s+)|(?:[?&](?:api[_-]?key|token|secret|password|auth(?:orization)?)=))[^\s&,"'}\]]+/gi;
|
|
12
|
+
const unsettledLoggers = new Set();
|
|
13
|
+
/** Resolves once every fallback logger created so far has finished its first-start removal and flushed its queue. */
|
|
14
|
+
export async function settledFallbackLoggers() {
|
|
15
|
+
await Promise.all([...unsettledLoggers]);
|
|
16
|
+
}
|
|
17
|
+
function errorCode(failure) {
|
|
18
|
+
if (typeof failure === "object" && failure !== null && "code" in failure && typeof failure.code === "string") {
|
|
19
|
+
return failure.code;
|
|
20
|
+
}
|
|
21
|
+
return "unknown";
|
|
22
|
+
}
|
|
10
23
|
export function createFallbackLogger(agentDir, options = {}) {
|
|
11
24
|
const filePath = join(agentDir, "logs", "fallback.log");
|
|
12
25
|
const maxBytes = validMaxBytes(options.maxBytes);
|
|
13
26
|
let reportedWriteFailure = false;
|
|
27
|
+
// Lines logged while the earlier generations are still being removed wait here, so the removal can never delete them.
|
|
28
|
+
// They are redacted before they enter the queue and the queue is bounded: the newest lines win, the drops are counted.
|
|
29
|
+
let queued = [];
|
|
30
|
+
let dropped = 0;
|
|
31
|
+
function write(line) {
|
|
32
|
+
try {
|
|
33
|
+
writeLine(filePath, line, maxBytes);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (!reportedWriteFailure) {
|
|
37
|
+
reportedWriteFailure = true;
|
|
38
|
+
console.error("Unable to write retry fallback debug log", error);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const settled = retireUnsharedRegistryGenerations(filePath)
|
|
43
|
+
.then(() => undefined)
|
|
44
|
+
.catch((error) => {
|
|
45
|
+
reportedWriteFailure = true;
|
|
46
|
+
console.error("Unable to remove earlier retry fallback debug log generations", error);
|
|
47
|
+
return error;
|
|
48
|
+
})
|
|
49
|
+
.then((failure) => {
|
|
50
|
+
const lines = queued ?? [];
|
|
51
|
+
queued = undefined;
|
|
52
|
+
for (const line of lines)
|
|
53
|
+
write(line);
|
|
54
|
+
if (dropped > 0) {
|
|
55
|
+
write(formatLine("warn", "queued_lines_dropped", {
|
|
56
|
+
reason: `${dropped} lines dropped while earlier log generations were removed`,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
if (failure !== undefined) {
|
|
60
|
+
// The code only: the error text names the agent dir's absolute path.
|
|
61
|
+
write(formatLine("warn", "retire_generations_failed", {
|
|
62
|
+
errorMessage: `removal failed: ${errorCode(failure)}`,
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
unsettledLoggers.add(settled);
|
|
67
|
+
void settled.then(() => unsettledLoggers.delete(settled));
|
|
14
68
|
function log(level, event, data) {
|
|
15
69
|
try {
|
|
16
70
|
const line = formatLine(level, event, data);
|
|
71
|
+
if (queued !== undefined) {
|
|
72
|
+
queued.push(redactSensitiveTokenValues(line, "[redacted]"));
|
|
73
|
+
if (queued.length > MAX_QUEUED_LINES) {
|
|
74
|
+
queued.shift();
|
|
75
|
+
dropped += 1;
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
17
79
|
writeLine(filePath, line, maxBytes);
|
|
18
80
|
}
|
|
19
81
|
catch (error) {
|
|
@@ -27,8 +89,23 @@ export function createFallbackLogger(agentDir, options = {}) {
|
|
|
27
89
|
debug: (event, data) => log("debug", event, data),
|
|
28
90
|
info: (event, data) => log("info", event, data),
|
|
29
91
|
warn: (event, data) => log("warn", event, data),
|
|
92
|
+
settled: () => settled,
|
|
30
93
|
};
|
|
31
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Generations written before every engine copy in a process shared one resolved-value registry (omo#12965) are
|
|
97
|
+
* removed once per agent dir, the first time an engine with the shared registry starts on it; the marker makes every
|
|
98
|
+
* later start leave the logs alone.
|
|
99
|
+
*/
|
|
100
|
+
async function retireUnsharedRegistryGenerations(filePath) {
|
|
101
|
+
const marker = join(dirname(filePath), ".fallback-log-v2");
|
|
102
|
+
if (await access(marker).then(() => true, () => false))
|
|
103
|
+
return;
|
|
104
|
+
await rm(`${filePath}.1`, { force: true });
|
|
105
|
+
await rm(filePath, { force: true });
|
|
106
|
+
await mkdir(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
107
|
+
await writeFile(marker, "", { mode: 0o600 });
|
|
108
|
+
}
|
|
32
109
|
function validMaxBytes(value) {
|
|
33
110
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : DEFAULT_MAX_BYTES;
|
|
34
111
|
}
|
|
@@ -51,7 +128,7 @@ function formatLine(level, event, data) {
|
|
|
51
128
|
}
|
|
52
129
|
function writeLine(filePath, line, maxBytes) {
|
|
53
130
|
mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });
|
|
54
|
-
const text = `${line}\n`;
|
|
131
|
+
const text = `${redactSensitiveTokenValues(line, "[redacted]")}\n`;
|
|
55
132
|
rotateLogIfNeeded(filePath, Buffer.byteLength(text), maxBytes);
|
|
56
133
|
const descriptor = openSync(filePath, "a", 0o600);
|
|
57
134
|
try {
|
|
@@ -66,13 +143,13 @@ function serializeValue(value, seen) {
|
|
|
66
143
|
if (typeof value === "string")
|
|
67
144
|
return safeText(value);
|
|
68
145
|
if (typeof value === "bigint")
|
|
69
|
-
return value.toString();
|
|
146
|
+
return safeText(value.toString());
|
|
70
147
|
if (typeof value === "number" || typeof value === "boolean" || value === null)
|
|
71
148
|
return value;
|
|
72
149
|
if (typeof value === "undefined")
|
|
73
150
|
return undefined;
|
|
74
151
|
if (typeof value === "function" || typeof value === "symbol")
|
|
75
|
-
return String(value);
|
|
152
|
+
return safeText(String(value));
|
|
76
153
|
if (seen.has(value))
|
|
77
154
|
return "[Circular]";
|
|
78
155
|
seen.add(value);
|
|
@@ -83,7 +160,7 @@ function serializeValue(value, seen) {
|
|
|
83
160
|
if (!BLOCKED_KEY.test(key)) {
|
|
84
161
|
const safeValue = serializeValue(item, seen);
|
|
85
162
|
if (safeValue !== undefined)
|
|
86
|
-
result[key] = safeValue;
|
|
163
|
+
result[safeText(key)] = safeValue;
|
|
87
164
|
}
|
|
88
165
|
}
|
|
89
166
|
return result;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { redactSecrets } from "../sensitive-output.js";
|
|
1
2
|
const EXCERPT_LIMIT = 500;
|
|
2
3
|
/**
|
|
3
4
|
* A model configuration error is never retried or fallen back: another model would answer a request the caller did
|
|
@@ -16,7 +17,7 @@ export function createModelConfigErrorReporter(logger, emit) {
|
|
|
16
17
|
if (reported.has(key))
|
|
17
18
|
return;
|
|
18
19
|
reported.add(key);
|
|
19
|
-
const errorMessage = message.errorMessage ?? "";
|
|
20
|
+
const errorMessage = redactSecrets(message.errorMessage ?? "");
|
|
20
21
|
logger.warn("model_config_error", { model, provider: message.provider, errorMessage });
|
|
21
22
|
emit({
|
|
22
23
|
type: "model_config_error",
|
|
@@ -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 = {};
|
|
@@ -440,6 +440,8 @@ export declare class SessionManager {
|
|
|
440
440
|
private _transcriptEntryBytes;
|
|
441
441
|
private _serializedFileEntries;
|
|
442
442
|
isPersisted(): boolean;
|
|
443
|
+
/** Give an unclaimed in-memory session its first file without rebuilding or changing its identity. */
|
|
444
|
+
enablePersistence(): Promise<void>;
|
|
443
445
|
getCwd(): string;
|
|
444
446
|
getSessionDir(): string;
|
|
445
447
|
usesDefaultSessionDir(): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getCurrentSystemMessage, normalizeProviderId } from "@earendil-works/pi-ai";
|
|
2
2
|
import { randomBytes, randomUUID } from "crypto";
|
|
3
3
|
import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync, readSync, rmSync, statSync, writeFileSync, } from "fs";
|
|
4
|
-
import { appendFile, open, readdir, rm } from "fs/promises";
|
|
4
|
+
import { appendFile, mkdir, open, readdir, rm } from "fs/promises";
|
|
5
5
|
import { basename, join, resolve } from "path";
|
|
6
6
|
import { StringDecoder } from "string_decoder";
|
|
7
7
|
import { APP_NAME, getAgentDir as getDefaultAgentDir, getSessionsDir } from "../config.js";
|
|
@@ -1230,6 +1230,20 @@ export class SessionManager {
|
|
|
1230
1230
|
isPersisted() {
|
|
1231
1231
|
return this.persist;
|
|
1232
1232
|
}
|
|
1233
|
+
/** Give an unclaimed in-memory session its first file without rebuilding or changing its identity. */
|
|
1234
|
+
async enablePersistence() {
|
|
1235
|
+
if (this.persist)
|
|
1236
|
+
return;
|
|
1237
|
+
const dir = getDefaultSessionDirPath(this.cwd);
|
|
1238
|
+
await mkdir(dir, { recursive: true });
|
|
1239
|
+
const header = this.fileEntries[0];
|
|
1240
|
+
const path = join(dir, `${header.timestamp.replace(/[:.]/g, "-")}_${this.sessionId}.jsonl`);
|
|
1241
|
+
reserveSessionWrite(path);
|
|
1242
|
+
this.sessionDir = dir;
|
|
1243
|
+
this.sessionFile = path;
|
|
1244
|
+
this.persist = true;
|
|
1245
|
+
registerSessionWriter(this);
|
|
1246
|
+
}
|
|
1233
1247
|
getCwd() {
|
|
1234
1248
|
return this.cwd;
|
|
1235
1249
|
}
|
|
@@ -302,6 +302,12 @@ export declare class SettingsManager {
|
|
|
302
302
|
getSettings(): Settings;
|
|
303
303
|
getGlobalSettings(): Settings;
|
|
304
304
|
getProjectSettings(): Settings;
|
|
305
|
+
/**
|
|
306
|
+
* `contextFiles.globalPath` from the user's own layers only: session overrides, then global
|
|
307
|
+
* settings. A project's `.senpi/settings.json` never redirects the user's global context file
|
|
308
|
+
* (senpi#5567), so the project layer is deliberately skipped.
|
|
309
|
+
*/
|
|
310
|
+
getGlobalContextFilePath(): string | undefined;
|
|
305
311
|
/** The project settings file at `path` as stored: parsed and migrated like a load, but never gated by trust. */
|
|
306
312
|
readProjectSettingsFile(path: string): ProjectSettingsFile;
|
|
307
313
|
/**
|
|
@@ -697,6 +697,14 @@ export class SettingsManager {
|
|
|
697
697
|
getProjectSettings() {
|
|
698
698
|
return structuredClone(this.projectSettings);
|
|
699
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* `contextFiles.globalPath` from the user's own layers only: session overrides, then global
|
|
702
|
+
* settings. A project's `.senpi/settings.json` never redirects the user's global context file
|
|
703
|
+
* (senpi#5567), so the project layer is deliberately skipped.
|
|
704
|
+
*/
|
|
705
|
+
getGlobalContextFilePath() {
|
|
706
|
+
return this.runtimeOverrides.contextFiles?.globalPath ?? this.globalSettings.contextFiles?.globalPath;
|
|
707
|
+
}
|
|
700
708
|
/** The project settings file at `path` as stored: parsed and migrated like a load, but never gated by trust. */
|
|
701
709
|
readProjectSettingsFile(path) {
|
|
702
710
|
let content;
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { SessionManager } from "./session-manager.ts";
|
|
2
1
|
/**
|
|
3
|
-
* Who started a turn, for the
|
|
4
|
-
*
|
|
5
|
-
* - `
|
|
2
|
+
* Who started a turn, for the remote approval-mode rule (senpi#6342: a turn follows the permission setting of the machine
|
|
3
|
+
* it runs on, and no origin outlives the turn it started):
|
|
4
|
+
* - `local`: this machine's own input, or input from a device the forwarder stated is the same account;
|
|
5
|
+
* - `delegated`: input from a device the forwarder did not state is the same account, or the one run such a device's
|
|
6
|
+
* record started;
|
|
6
7
|
* - `engine`: a turn no client record started (an extension's message, a goal or loop turn, restored input).
|
|
7
8
|
* A steer joins the running turn (`join`) and never changes its origin.
|
|
8
9
|
*/
|
|
9
10
|
export type TurnOrigin = "local" | "delegated" | "engine";
|
|
10
11
|
export type InputTurnMark = TurnOrigin | "join";
|
|
11
12
|
export type ConnectionOrigin = "local" | "delegated";
|
|
12
|
-
type OriginSessionManager = Pick<SessionManager, "getEntries" | "appendCustomEntry">;
|
|
13
13
|
/** The mark an input gets where it is created: explicit for client records, `local` for the TUI, else `engine`. */
|
|
14
14
|
export declare function inputTurnMark(input: {
|
|
15
15
|
readonly mode?: "steer" | "followUp";
|
|
@@ -21,11 +21,19 @@ export declare function markInputTurnOrigin(message: object, mark: InputTurnMark
|
|
|
21
21
|
export declare function copyInputTurnMark(from: object, to: object): void;
|
|
22
22
|
/** Whether a message carries an origin mark: a user message always opens a turn's origin, any other only when marked. */
|
|
23
23
|
export declare function hasInputTurnMark(message: object): boolean;
|
|
24
|
-
/**
|
|
24
|
+
/** Runs `handle`, and every async task it starts, as the record `origin` describes. */
|
|
25
|
+
export declare function runInRecordScope<T>(origin: ConnectionOrigin, handle: () => Promise<T>): Promise<T>;
|
|
26
|
+
/** Whether the work being created belongs to an open cross-account record's own handling. */
|
|
27
|
+
export declare function recordIsDelegated(): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* A run starts as engine work, or as delegated work when a cross-account record's own handling starts it; a retry keeps
|
|
30
|
+
* the turn it continues. The user message that opens a turn sets its origin, a steer keeps it.
|
|
31
|
+
*/
|
|
25
32
|
export declare function noteRunStart(sessionManager: object): void;
|
|
33
|
+
/** A retry or recovery run repeats the turn it continues, so it keeps that turn's origin until the continuation ends. */
|
|
34
|
+
export declare function noteTurnContinuation(sessionManager: object): void;
|
|
35
|
+
export declare function endTurnContinuation(sessionManager: object): void;
|
|
26
36
|
export declare function noteUserMessageStart(sessionManager: object, message: object): void;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
export declare function decidingTurnIsDelegated(sessionManager: Pick<SessionManager, "getEntries">): boolean;
|
|
30
|
-
export {};
|
|
37
|
+
/** Whether the deciding turn counts as delegated-origin: a cross-account device started this turn or this run. */
|
|
38
|
+
export declare function decidingTurnIsDelegated(sessionManager: object): boolean;
|
|
31
39
|
//# sourceMappingURL=turn-origin.d.ts.map
|
package/dist/core/turn-origin.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
const inputMarks = new WeakMap();
|
|
3
3
|
const currentOrigins = new WeakMap();
|
|
4
|
-
const
|
|
4
|
+
const recordScopes = new AsyncLocalStorage();
|
|
5
|
+
const continuingTurns = new WeakSet();
|
|
5
6
|
/** The mark an input gets where it is created: explicit for client records, `local` for the TUI, else `engine`. */
|
|
6
7
|
export function inputTurnMark(input) {
|
|
7
8
|
if (input.mode === "steer")
|
|
8
9
|
return "join";
|
|
9
10
|
if (input.connectionOrigin !== undefined)
|
|
10
11
|
return input.connectionOrigin;
|
|
12
|
+
if (recordIsDelegated())
|
|
13
|
+
return "delegated";
|
|
11
14
|
return input.source === undefined || input.source === "interactive" ? "local" : "engine";
|
|
12
15
|
}
|
|
13
16
|
export function markInputTurnOrigin(message, mark) {
|
|
@@ -23,9 +26,34 @@ export function copyInputTurnMark(from, to) {
|
|
|
23
26
|
export function hasInputTurnMark(message) {
|
|
24
27
|
return inputMarks.has(message);
|
|
25
28
|
}
|
|
26
|
-
/**
|
|
29
|
+
/** Runs `handle`, and every async task it starts, as the record `origin` describes. */
|
|
30
|
+
export function runInRecordScope(origin, handle) {
|
|
31
|
+
return recordScopes.run({ origin, open: true }, handle);
|
|
32
|
+
}
|
|
33
|
+
/** Whether the work being created belongs to an open cross-account record's own handling. */
|
|
34
|
+
export function recordIsDelegated() {
|
|
35
|
+
const scope = recordScopes.getStore();
|
|
36
|
+
return scope?.open === true && scope.origin === "delegated";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A run starts as engine work, or as delegated work when a cross-account record's own handling starts it; a retry keeps
|
|
40
|
+
* the turn it continues. The user message that opens a turn sets its origin, a steer keeps it.
|
|
41
|
+
*/
|
|
27
42
|
export function noteRunStart(sessionManager) {
|
|
28
|
-
|
|
43
|
+
if (continuingTurns.has(sessionManager))
|
|
44
|
+
return;
|
|
45
|
+
const delegated = recordIsDelegated();
|
|
46
|
+
const scope = recordScopes.getStore();
|
|
47
|
+
if (scope !== undefined)
|
|
48
|
+
scope.open = false;
|
|
49
|
+
currentOrigins.set(sessionManager, delegated ? "delegated" : "engine");
|
|
50
|
+
}
|
|
51
|
+
/** A retry or recovery run repeats the turn it continues, so it keeps that turn's origin until the continuation ends. */
|
|
52
|
+
export function noteTurnContinuation(sessionManager) {
|
|
53
|
+
continuingTurns.add(sessionManager);
|
|
54
|
+
}
|
|
55
|
+
export function endTurnContinuation(sessionManager) {
|
|
56
|
+
continuingTurns.delete(sessionManager);
|
|
29
57
|
}
|
|
30
58
|
export function noteUserMessageStart(sessionManager, message) {
|
|
31
59
|
const mark = inputMarks.get(message);
|
|
@@ -33,32 +61,8 @@ export function noteUserMessageStart(sessionManager, message) {
|
|
|
33
61
|
return;
|
|
34
62
|
currentOrigins.set(sessionManager, mark ?? "engine");
|
|
35
63
|
}
|
|
36
|
-
|
|
37
|
-
if (delegatedOriginOf(sessionManager))
|
|
38
|
-
return;
|
|
39
|
-
delegatedSessions.add(sessionManager);
|
|
40
|
-
sessionManager.appendCustomEntry(DELEGATED_ORIGIN_ENTRY, { delegatedOrigin: true }, { startupBookkeeping: true });
|
|
41
|
-
}
|
|
42
|
-
// How many entries a session's scan has already found free of the marker: each gated decision reads only new entries.
|
|
43
|
-
const scannedEntries = new WeakMap();
|
|
44
|
-
function delegatedOriginOf(sessionManager) {
|
|
45
|
-
if (delegatedSessions.has(sessionManager))
|
|
46
|
-
return true;
|
|
47
|
-
const entries = sessionManager.getEntries();
|
|
48
|
-
const persisted = entries
|
|
49
|
-
.slice(scannedEntries.get(sessionManager) ?? 0)
|
|
50
|
-
.some((entry) => entry.type === "custom" && entry.customType === DELEGATED_ORIGIN_ENTRY);
|
|
51
|
-
if (persisted)
|
|
52
|
-
delegatedSessions.add(sessionManager);
|
|
53
|
-
else
|
|
54
|
-
scannedEntries.set(sessionManager, entries.length);
|
|
55
|
-
return persisted;
|
|
56
|
-
}
|
|
57
|
-
/** Whether the deciding turn counts as delegated-origin: a remote device started it, or nobody did on a remote-driven session. */
|
|
64
|
+
/** Whether the deciding turn counts as delegated-origin: a cross-account device started this turn or this run. */
|
|
58
65
|
export function decidingTurnIsDelegated(sessionManager) {
|
|
59
|
-
|
|
60
|
-
if (origin === "delegated")
|
|
61
|
-
return true;
|
|
62
|
-
return origin === "engine" && delegatedOriginOf(sessionManager);
|
|
66
|
+
return currentOrigins.get(sessionManager) === "delegated";
|
|
63
67
|
}
|
|
64
68
|
//# sourceMappingURL=turn-origin.js.map
|
|
@@ -4,5 +4,6 @@ export declare function serviceTierForProvider(provider: string | undefined, ser
|
|
|
4
4
|
export declare function ultrafastSelectionWarning(model: {
|
|
5
5
|
readonly provider: string;
|
|
6
6
|
readonly id: string;
|
|
7
|
+
readonly upstreamModelId?: string;
|
|
7
8
|
}, serviceTier: ServiceTier | undefined): string | undefined;
|
|
8
9
|
//# sourceMappingURL=ultrafast-lanes.d.ts.map
|
|
@@ -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;
|