@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deadlineStopPendingSinceLastTurn } from "../../../turn-deadline.js";
|
|
2
|
+
import { subscribeProviderAccountEvents } from "../anthropic-subscription/account-events.js";
|
|
2
3
|
import { isHumanInputMonitorNotification } from "../monitor-state-event.js";
|
|
3
4
|
import { createGoalCacheWarmScheduleData, estimateCacheWarmMetrics, GOAL_CACHE_WARMUP_ENTRY_TYPE, GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS, resolveGoalBackstopMaxSecondsForCache, resolveGoalMonitorContinuationDelayMs, resolveGoalPromptCacheLifetime, } from "./cache-warm.js";
|
|
4
5
|
import { subscribeGoalChannelState } from "./channel-state-subscriptions.js";
|
|
@@ -9,7 +10,7 @@ import { lastAssistantMessage } from "./last-assistant-message.js";
|
|
|
9
10
|
import { admitAndQueueGoalContinuation, buildCurrentGoalContinuationSignature, isLastTurnStuckOnContextOverflow, lastAssistantText, } from "./lifecycle-helpers.js";
|
|
10
11
|
import { findParkedGoalWait } from "./parked-wait.js";
|
|
11
12
|
import { buildContinuationPrompt, buildGoalStallNotice, buildTruncationRecoveryPrompt } from "./prompt.js";
|
|
12
|
-
import { exhaustedLadderResumeDelayMs, PROVIDER_RETRY_BASE_MS, providerRetryAt, providerRetryDelayMs, } from "./provider-retry.js";
|
|
13
|
+
import { exhaustedLadderResumeDelayMs, GOAL_LIMIT_WAIT_WAKE_LIMIT, isLimitRefusal, limitWaitFloorMs, PROVIDER_RETRY_BASE_MS, providerRetryAt, providerRetryDelayMs, } from "./provider-retry.js";
|
|
13
14
|
import { isStaleExtensionContextError } from "./stale-context.js";
|
|
14
15
|
import { resetContinuationStreak } from "./store.js";
|
|
15
16
|
import { goalStoreRef } from "./store-ref.js";
|
|
@@ -22,6 +23,8 @@ export const GOAL_CONTINUATION_SCHEDULED_EVENT = "goal_continuation_scheduled";
|
|
|
22
23
|
export const GOAL_CONTINUATION_RESUMED_EVENT = "goal_continuation_resumed";
|
|
23
24
|
export const GOAL_CONTINUATION_TIMER_STATE_EVENT = "goal_continuation_timer_state";
|
|
24
25
|
export const GOAL_MONITOR_STALL_EVENT = "goal_monitor_continuation_stall";
|
|
26
|
+
/** A limit outlasted GOAL_LIMIT_WAIT_WAKE_LIMIT re-checks: the goal waits for a sign-in, a block reset or input. */
|
|
27
|
+
const GOAL_LIMIT_WAIT_PARKED_EVENT = "goal_limit_wait_parked";
|
|
25
28
|
/** The one notice a provider-limit park shows: when the goal resumes, in local time and as the exact instant. */
|
|
26
29
|
function providerPauseNotice(retryAt) {
|
|
27
30
|
const local = new Date(retryAt).toLocaleString("en-US", {
|
|
@@ -68,12 +71,51 @@ export class MonitorAwareGoalContinuation {
|
|
|
68
71
|
#pendingProviderRecovery;
|
|
69
72
|
#providerRetryAttempts = 0;
|
|
70
73
|
#providerRetryDelayMs;
|
|
74
|
+
/** The armed provider retry waits out a usage or rate limit, so its wake spends no continuation budget (senpi#4085). */
|
|
75
|
+
#limitWait = false;
|
|
76
|
+
#limitWakes = 0;
|
|
77
|
+
#limitParked = false;
|
|
71
78
|
constructor(pi, isContinuationPending = () => false, markContinuationPending = () => { }, waitTicker) {
|
|
72
79
|
this.#pi = pi;
|
|
73
80
|
this.#isContinuationPending = isContinuationPending;
|
|
74
81
|
this.#markContinuationPending = markContinuationPending;
|
|
75
82
|
this.#waitTicker = waitTicker;
|
|
76
83
|
this.#subscribeToChannelState();
|
|
84
|
+
// A login or an explicit block reset can end the wait early: re-check the provider now (senpi#4085).
|
|
85
|
+
this.#channelStateUnsubscribers.push(subscribeProviderAccountEvents((event) => {
|
|
86
|
+
if (event.type === "accounts_changed" && event.remedy !== undefined)
|
|
87
|
+
this.#wakeLimitWait();
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
#wakeLimitWait() {
|
|
91
|
+
const goal = this.#goal;
|
|
92
|
+
const waiting = this.#scheduledContinuationKind === "providerRetry" || this.#limitParked;
|
|
93
|
+
if (!this.#limitWait || !waiting || goal === null)
|
|
94
|
+
return;
|
|
95
|
+
this.#limitWakes = 0;
|
|
96
|
+
this.#limitParked = false;
|
|
97
|
+
this.#cancelTimer();
|
|
98
|
+
this.#providerRetryDelayMs = 0;
|
|
99
|
+
this.#schedule(goal, "providerRetry");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Arms the next re-check of a limit wait, never sooner than the provider backoff. A limit that outlasts
|
|
103
|
+
* GOAL_LIMIT_WAIT_WAKE_LIMIT re-checks parks the goal, still active, until a sign-in, a block reset or input.
|
|
104
|
+
*/
|
|
105
|
+
#armLimitWait(goal, delayMs) {
|
|
106
|
+
this.#limitWait = true;
|
|
107
|
+
this.#limitWakes += 1;
|
|
108
|
+
if (this.#limitWakes > GOAL_LIMIT_WAIT_WAKE_LIMIT) {
|
|
109
|
+
this.#limitParked = true;
|
|
110
|
+
this.#publish(GOAL_LIMIT_WAIT_PARKED_EVENT, { goalId: goal.id, wakes: GOAL_LIMIT_WAIT_WAKE_LIMIT });
|
|
111
|
+
if (this.#ctxHasUI()) {
|
|
112
|
+
this.#ctx?.ui.notify(`Goal paused: the provider kept refusing for a usage limit through ${GOAL_LIMIT_WAIT_WAKE_LIMIT} re-checks. It resumes after a sign-in, an account block reset, or your next message.`, "warning");
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
this.#providerRetryDelayMs = Math.max(delayMs, limitWaitFloorMs(this.#providerRetryAttempts));
|
|
117
|
+
this.#schedule(goal, "providerRetry");
|
|
118
|
+
return true;
|
|
77
119
|
}
|
|
78
120
|
start(ctx) {
|
|
79
121
|
this.#cancelTimer();
|
|
@@ -91,6 +133,9 @@ export class MonitorAwareGoalContinuation {
|
|
|
91
133
|
if (options.goal?.id !== this.#goal?.id)
|
|
92
134
|
this.#resetContinuationState();
|
|
93
135
|
this.#providerRetryAttempts = 0;
|
|
136
|
+
this.#limitWait = false;
|
|
137
|
+
this.#limitWakes = 0;
|
|
138
|
+
this.#limitParked = false;
|
|
94
139
|
this.#ctx = options.ctx;
|
|
95
140
|
this.#goal = options.goal;
|
|
96
141
|
this.#lastAgentEndMessages = options.messages;
|
|
@@ -214,9 +259,18 @@ export class MonitorAwareGoalContinuation {
|
|
|
214
259
|
return undefined;
|
|
215
260
|
}
|
|
216
261
|
this.#providerRetryAttempts += 1;
|
|
262
|
+
this.#limitWait = isLimitRefusal(pendingProvider.messages);
|
|
217
263
|
const now = Date.now();
|
|
218
264
|
const retryAt = providerRetryAt(pendingProvider.messages, now);
|
|
219
265
|
const delayMs = providerRetryDelayMs(this.#providerRetryAttempts, retryAt, now);
|
|
266
|
+
if (this.#limitWait) {
|
|
267
|
+
this.#cancelTimer();
|
|
268
|
+
const armed = this.#armLimitWait(pendingProvider.goal, delayMs);
|
|
269
|
+
if (armed && retryAt !== undefined && this.#ctxHasUI(pendingProvider.ctx)) {
|
|
270
|
+
pendingProvider.ctx.ui.notify(providerPauseNotice(retryAt), "info");
|
|
271
|
+
}
|
|
272
|
+
return pendingProvider.goal;
|
|
273
|
+
}
|
|
220
274
|
if (delayMs > 0) {
|
|
221
275
|
this.#cancelTimer();
|
|
222
276
|
this.#providerRetryDelayMs = delayMs;
|
|
@@ -230,17 +284,16 @@ export class MonitorAwareGoalContinuation {
|
|
|
230
284
|
}
|
|
231
285
|
/**
|
|
232
286
|
* senpi#3709: every fallback rung failed, so a continuation now would hit the same limits. Park on
|
|
233
|
-
* the soonest reset the run named
|
|
287
|
+
* the soonest reset the run named; with no known reset, re-check on the provider backoff (senpi#4085).
|
|
234
288
|
*/
|
|
235
289
|
#parkUntilLadderReset(resumeAt) {
|
|
236
290
|
const goal = this.#goal;
|
|
237
291
|
if (goal?.status !== "active")
|
|
238
292
|
return undefined;
|
|
239
293
|
this.#cancelTimer();
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
this.#
|
|
243
|
-
this.#schedule(goal, "providerRetry");
|
|
294
|
+
this.#providerRetryAttempts += 1;
|
|
295
|
+
// No failure named a reset: re-check on the provider backoff (15 s doubling to 30 min) rather than wait for input.
|
|
296
|
+
this.#armLimitWait(goal, resumeAt === undefined ? 0 : exhaustedLadderResumeDelayMs(resumeAt, Date.now()));
|
|
244
297
|
return goal;
|
|
245
298
|
}
|
|
246
299
|
syncGoal(goal) {
|
|
@@ -521,7 +574,10 @@ export class MonitorAwareGoalContinuation {
|
|
|
521
574
|
this.#resetCacheWarmIteration();
|
|
522
575
|
}
|
|
523
576
|
async #admitAndQueue(ctx, goal, path, messages) {
|
|
524
|
-
const input =
|
|
577
|
+
const input = {
|
|
578
|
+
...this.#buildVerdictInput(ctx, goal, path, messages),
|
|
579
|
+
...(path === "providerRecovery" && this.#limitWait ? { limitWait: true } : {}),
|
|
580
|
+
};
|
|
525
581
|
const verdict = evaluateGoalContinuation({ goal, ...input });
|
|
526
582
|
const admittedGoal = await admitAndQueueGoalContinuation(this.#pi, ctx, goal, {
|
|
527
583
|
input,
|
|
@@ -747,6 +803,9 @@ export class MonitorAwareGoalContinuation {
|
|
|
747
803
|
this.#pendingSystemRecovery = undefined;
|
|
748
804
|
this.#pendingProviderRecovery = undefined;
|
|
749
805
|
this.#providerRetryAttempts = 0;
|
|
806
|
+
this.#limitWait = false;
|
|
807
|
+
this.#limitWakes = 0;
|
|
808
|
+
this.#limitParked = false;
|
|
750
809
|
this.#consecutiveLengthRecoveries.clear();
|
|
751
810
|
this.#recentNormalizedOutputHashes = [];
|
|
752
811
|
this.#recentToolEffects = [];
|
|
@@ -318,8 +318,28 @@ function sanitizeContinuationState(goal) {
|
|
|
318
318
|
delete next.lastContinuationSignature;
|
|
319
319
|
if (!isNonNegativeSafeInteger(next.continuationStoppedAt))
|
|
320
320
|
delete next.continuationStoppedAt;
|
|
321
|
+
if (!isValidContinuationStoppedDetail(next.continuationStoppedDetail)) {
|
|
322
|
+
delete next.continuationStoppedDetail;
|
|
323
|
+
}
|
|
324
|
+
// The detail only means something with its claim; never keep a stale payload past the claim.
|
|
325
|
+
if (next.continuationStoppedAt === undefined)
|
|
326
|
+
delete next.continuationStoppedDetail;
|
|
321
327
|
return next;
|
|
322
328
|
}
|
|
329
|
+
/** A stop-publication payload is usable only with a known deny reason and non-negative counters. */
|
|
330
|
+
function isValidContinuationStoppedDetail(value) {
|
|
331
|
+
if (!isRecord(value))
|
|
332
|
+
return false;
|
|
333
|
+
return ((value.reason === "cap" ||
|
|
334
|
+
value.reason === "stale" ||
|
|
335
|
+
value.reason === "repetition" ||
|
|
336
|
+
value.reason === "length-exhausted" ||
|
|
337
|
+
value.reason === "no-progress" ||
|
|
338
|
+
value.reason === "context-overflow") &&
|
|
339
|
+
isNonNegativeSafeInteger(value.consecutiveContinuations) &&
|
|
340
|
+
isNonNegativeSafeInteger(value.unattendedContinuations) &&
|
|
341
|
+
isNonNegativeSafeInteger(value.noProgressContinuations));
|
|
342
|
+
}
|
|
323
343
|
function isMissingFile(error) {
|
|
324
344
|
return isFileSystemError(error, "ENOENT");
|
|
325
345
|
}
|
|
@@ -7,6 +7,15 @@ export declare const PROVIDER_RETRY_WAKE_CAP_MS: number;
|
|
|
7
7
|
* pool guidance falls back to the earliest future ISO instant in the error only.
|
|
8
8
|
*/
|
|
9
9
|
export declare function providerRetryAt(messages: readonly AgentMessage[], now: number): number | undefined;
|
|
10
|
+
/** Re-checks of a limit that never clears before the goal parks until a sign-in, a block reset or input (senpi#4085). */
|
|
11
|
+
export declare const GOAL_LIMIT_WAIT_WAKE_LIMIT = 48;
|
|
12
|
+
/** The shortest wait before the `attempt`-th limit re-check: the provider backoff, 15 s doubling to 30 min. */
|
|
13
|
+
export declare function limitWaitFloorMs(attempt: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* The turn ended on a provider refusing for a usage or rate limit, not on an outage (senpi#4085). An OpenAI-compatible
|
|
16
|
+
* adapter reports a bare 429 as a status-only diagnostic (`unknown` + 429); that status is still a limit.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isLimitRefusal(messages: readonly AgentMessage[]): boolean;
|
|
10
19
|
/**
|
|
11
20
|
* How long to wait before the `attempt`-th consecutive provider-recovery
|
|
12
21
|
* continuation (senpi#3264). A stated unblock time is the wake; otherwise the
|
|
@@ -31,6 +31,22 @@ export function providerRetryAt(messages, now) {
|
|
|
31
31
|
.filter((at) => Number.isFinite(at) && at > now);
|
|
32
32
|
return instants.length === 0 ? undefined : Math.min(...instants);
|
|
33
33
|
}
|
|
34
|
+
/** Re-checks of a limit that never clears before the goal parks until a sign-in, a block reset or input (senpi#4085). */
|
|
35
|
+
export const GOAL_LIMIT_WAIT_WAKE_LIMIT = 48;
|
|
36
|
+
/** The shortest wait before the `attempt`-th limit re-check: the provider backoff, 15 s doubling to 30 min. */
|
|
37
|
+
export function limitWaitFloorMs(attempt) {
|
|
38
|
+
return Math.min(PROVIDER_RETRY_CAP_MS, PROVIDER_RETRY_BASE_MS * 2 ** (Math.max(1, attempt) - 1));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The turn ended on a provider refusing for a usage or rate limit, not on an outage (senpi#4085). An OpenAI-compatible
|
|
42
|
+
* adapter reports a bare 429 as a status-only diagnostic (`unknown` + 429); that status is still a limit.
|
|
43
|
+
*/
|
|
44
|
+
export function isLimitRefusal(messages) {
|
|
45
|
+
const diagnostic = lastAssistantMessage(messages)?.providerDiagnostic;
|
|
46
|
+
return (diagnostic?.category === "rate_limit" ||
|
|
47
|
+
diagnostic?.category === "quota" ||
|
|
48
|
+
(diagnostic?.category === "unknown" && diagnostic.httpStatus === 429));
|
|
49
|
+
}
|
|
34
50
|
/**
|
|
35
51
|
* How long to wait before the `attempt`-th consecutive provider-recovery
|
|
36
52
|
* continuation (senpi#3264). A stated unblock time is the wake; otherwise the
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExtensionContext } from "../../types.ts";
|
|
2
2
|
import type { MonitorAwareGoalContinuation } from "./monitor-continuation.ts";
|
|
3
|
+
import type { TrailingGoalContinuations } from "./session-start-continuation.ts";
|
|
3
4
|
import type { Goal } from "./types.ts";
|
|
4
5
|
export type ReloadReengagementOutcome = "skipped-inactive" | "backstop-rearmed" | "suppressed-flood" | "continuation-queued";
|
|
5
6
|
/**
|
|
@@ -24,7 +25,7 @@ export declare function reengageGoalAfterReload(options: {
|
|
|
24
25
|
readonly ctx: ExtensionContext;
|
|
25
26
|
readonly goal: Goal;
|
|
26
27
|
readonly monitor: MonitorAwareGoalContinuation;
|
|
27
|
-
readonly
|
|
28
|
-
readonly queueContinuation: () => Promise<void>;
|
|
28
|
+
readonly readTrailingContinuations: () => TrailingGoalContinuations;
|
|
29
|
+
readonly queueContinuation: (limitWait: boolean) => Promise<void>;
|
|
29
30
|
}): Promise<ReloadReengagementOutcome>;
|
|
30
31
|
//# sourceMappingURL=reload-reengagement.d.ts.map
|
|
@@ -30,12 +30,12 @@ export async function reengageGoalAfterReload(options) {
|
|
|
30
30
|
monitor.rearmMonitorBackstop(goal);
|
|
31
31
|
return "backstop-rearmed";
|
|
32
32
|
}
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
35
|
-
ctx.ui.notify(`Goal auto-continuation suppressed for this resumed session (${
|
|
33
|
+
const trailing = options.readTrailingContinuations();
|
|
34
|
+
if (trailing.counted >= GOAL_CONTINUATION_CAP) {
|
|
35
|
+
ctx.ui.notify(`Goal auto-continuation suppressed for this resumed session (${trailing.counted} historical continuations). Send a message to resume.`, "info");
|
|
36
36
|
return "suppressed-flood";
|
|
37
37
|
}
|
|
38
|
-
await options.queueContinuation();
|
|
38
|
+
await options.queueContinuation(trailing.waitingOutLimit);
|
|
39
39
|
return "continuation-queued";
|
|
40
40
|
}
|
|
41
41
|
//# sourceMappingURL=reload-reengagement.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { SessionEntry } from "../../../session-manager.ts";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "../../types.ts";
|
|
3
|
+
import type { Goal } from "./types.ts";
|
|
4
|
+
export type TrailingGoalContinuations = {
|
|
5
|
+
/** Continuations whose turn ran: the unattended loop the load-time flood guard bounds. */
|
|
6
|
+
readonly counted: number;
|
|
7
|
+
/** The newest continuation was refused for a usage or rate limit, so the goal was waiting that limit out. */
|
|
8
|
+
readonly waitingOutLimit: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type SessionStartContinuationOptions = {
|
|
11
|
+
readonly continuationPending: boolean;
|
|
12
|
+
readonly markContinuationPending: () => void;
|
|
13
|
+
/** The session opened in the middle of a limit wait, so this wake re-checks that limit (senpi#4085). */
|
|
14
|
+
readonly limitWait?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Reads the goal continuations queued since the most recent real user message in the branch (mirrors the
|
|
18
|
+
* todo-gate / todo-bridge backward branch read). A real user message is the only reset: the assistant turns in
|
|
19
|
+
* between are exactly the unattended loop the load-time admission guard exists to stop.
|
|
20
|
+
*
|
|
21
|
+
* A continuation the provider refused for a usage or rate limit ran no model turn, so it is not part of that
|
|
22
|
+
* loop (senpi#4085). A goal that waited a limit out for more wakes than the cap is therefore not a flood, and it
|
|
23
|
+
* resumes after a restart or a config reload.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readTrailingGoalContinuations(entries: readonly SessionEntry[]): TrailingGoalContinuations;
|
|
26
|
+
/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */
|
|
27
|
+
export declare function queueGoalContinuation(pi: ExtensionAPI, ctx: ExtensionContext, goal: Goal, options: SessionStartContinuationOptions): Promise<Goal | null>;
|
|
28
|
+
//# sourceMappingURL=session-start-continuation.d.ts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { GOAL_CONTINUATION_MESSAGE_TYPE } from "../../../messages.js";
|
|
2
|
+
import { admitAndQueueGoalContinuation, buildCurrentGoalContinuationSignature, isLastTurnStuckOnContextOverflow, lastAssistantText, } from "./lifecycle-helpers.js";
|
|
3
|
+
import { buildContinuationPrompt } from "./prompt.js";
|
|
4
|
+
import { isLimitRefusal } from "./provider-retry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Reads the goal continuations queued since the most recent real user message in the branch (mirrors the
|
|
7
|
+
* todo-gate / todo-bridge backward branch read). A real user message is the only reset: the assistant turns in
|
|
8
|
+
* between are exactly the unattended loop the load-time admission guard exists to stop.
|
|
9
|
+
*
|
|
10
|
+
* A continuation the provider refused for a usage or rate limit ran no model turn, so it is not part of that
|
|
11
|
+
* loop (senpi#4085). A goal that waited a limit out for more wakes than the cap is therefore not a flood, and it
|
|
12
|
+
* resumes after a restart or a config reload.
|
|
13
|
+
*/
|
|
14
|
+
export function readTrailingGoalContinuations(entries) {
|
|
15
|
+
let counted = 0;
|
|
16
|
+
let waitingOutLimit;
|
|
17
|
+
let turn = [];
|
|
18
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
19
|
+
const entry = entries[index];
|
|
20
|
+
if (entry?.type === "message") {
|
|
21
|
+
if (entry.message.role === "user")
|
|
22
|
+
break;
|
|
23
|
+
if (entry.message.role === "assistant")
|
|
24
|
+
turn.push(entry.message);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (entry?.type !== "custom_message" || entry.customType !== GOAL_CONTINUATION_MESSAGE_TYPE)
|
|
28
|
+
continue;
|
|
29
|
+
const refused = wasRefusedForLimit(turn);
|
|
30
|
+
waitingOutLimit ??= refused;
|
|
31
|
+
if (!refused)
|
|
32
|
+
counted += 1;
|
|
33
|
+
turn = [];
|
|
34
|
+
}
|
|
35
|
+
return { counted, waitingOutLimit: waitingOutLimit ?? false };
|
|
36
|
+
}
|
|
37
|
+
function wasRefusedForLimit(turn) {
|
|
38
|
+
return (turn.length > 0 &&
|
|
39
|
+
turn.every((message) => message.stopReason === "error") &&
|
|
40
|
+
turn.some((message) => isLimitRefusal([message])));
|
|
41
|
+
}
|
|
42
|
+
/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */
|
|
43
|
+
export async function queueGoalContinuation(pi, ctx, goal, options) {
|
|
44
|
+
const lastAssistant = lastAssistantFromEntries(ctx.sessionManager.getBranch());
|
|
45
|
+
const signature = buildCurrentGoalContinuationSignature(ctx, goal, lastAssistant === undefined ? "" : lastAssistantText([lastAssistant]));
|
|
46
|
+
return admitAndQueueGoalContinuation(pi, ctx, goal, {
|
|
47
|
+
input: {
|
|
48
|
+
isIdle: ctx.isIdle(),
|
|
49
|
+
hasPendingMessages: ctx.hasPendingMessages(),
|
|
50
|
+
path: "sessionStart",
|
|
51
|
+
lastStopReason: undefined,
|
|
52
|
+
lastTurnWasMalformedToolUse: false,
|
|
53
|
+
consecutiveContinuations: goal.consecutiveContinuations ?? 0,
|
|
54
|
+
lastContinuationSignature: goal.lastContinuationSignature,
|
|
55
|
+
currentSignature: signature,
|
|
56
|
+
consecutiveLengthRecoveries: 0,
|
|
57
|
+
recentNormalizedOutputHashes: [],
|
|
58
|
+
toollessContinuationStreak: 0,
|
|
59
|
+
continuationPending: options.continuationPending,
|
|
60
|
+
lastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(ctx, lastAssistant),
|
|
61
|
+
...(options.limitWait === true ? { limitWait: true } : {}),
|
|
62
|
+
},
|
|
63
|
+
content: () => buildContinuationPrompt(goal, { modelId: ctx.model?.id }),
|
|
64
|
+
markContinuationPending: options.markContinuationPending,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function lastAssistantFromEntries(entries) {
|
|
68
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
69
|
+
const entry = entries[index];
|
|
70
|
+
if (entry?.type === "message" && entry.message.role === "assistant")
|
|
71
|
+
return entry.message;
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=session-start-continuation.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { goalFilePath } from "./persistence.ts";
|
|
2
|
-
import type { Goal, GoalAccountingMode, GoalStoreRef, GoalUpdate, GoalUpdateSource, TokenUsageSnapshot } from "./types.ts";
|
|
2
|
+
import type { Goal, GoalAccountingMode, GoalContinuationStoppedDetail, GoalStoreRef, GoalUpdate, GoalUpdateSource, TokenUsageSnapshot } from "./types.ts";
|
|
3
3
|
export { goalFilePath };
|
|
4
4
|
export declare function goalHistoryFilePath(ref: GoalStoreRef): string;
|
|
5
5
|
export declare function objectiveFullTextFileName(ref: GoalStoreRef): string;
|
|
@@ -15,13 +15,14 @@ export declare function clearGoal(ref: GoalStoreRef): Promise<boolean>;
|
|
|
15
15
|
export declare function accountGoalUsage(ref: GoalStoreRef, usage: TokenUsageSnapshot, elapsedSeconds: number, mode?: GoalAccountingMode, expectedGoalId?: string): Promise<Goal | null>;
|
|
16
16
|
export declare function recordContinuationDelivered(ref: GoalStoreRef, signature: string, expectedGoalId?: string, options?: {
|
|
17
17
|
countUnattended?: boolean;
|
|
18
|
+
countStreak?: boolean;
|
|
18
19
|
}): Promise<Goal | null>;
|
|
19
20
|
export declare function resetContinuationStreak(ref: GoalStoreRef, options?: {
|
|
20
21
|
unattended?: boolean;
|
|
21
22
|
progress?: boolean;
|
|
22
23
|
}): Promise<Goal | null>;
|
|
23
24
|
/** Claims one denial under the same lock as input/reset, so evaluation retries cannot append another stop. */
|
|
24
|
-
export declare function recordGoalContinuationStopped(ref: GoalStoreRef, expected: Goal, at: number, blockedReason: string | undefined): Promise<{
|
|
25
|
+
export declare function recordGoalContinuationStopped(ref: GoalStoreRef, expected: Goal, at: number, blockedReason: string | undefined, detail: GoalContinuationStoppedDetail): Promise<{
|
|
25
26
|
goal: Goal | null;
|
|
26
27
|
recorded: boolean;
|
|
27
28
|
}>;
|
|
@@ -111,6 +111,7 @@ export async function updateGoal(ref, update, source = "model") {
|
|
|
111
111
|
next.noProgressContinuations = 0;
|
|
112
112
|
delete next.lastContinuationSignature;
|
|
113
113
|
delete next.continuationStoppedAt;
|
|
114
|
+
delete next.continuationStoppedDetail;
|
|
114
115
|
}
|
|
115
116
|
if (source === "user" && requestedStatus === "active") {
|
|
116
117
|
next.consecutiveContinuations = 0;
|
|
@@ -118,6 +119,7 @@ export async function updateGoal(ref, update, source = "model") {
|
|
|
118
119
|
next.noProgressContinuations = 0;
|
|
119
120
|
delete next.lastContinuationSignature;
|
|
120
121
|
delete next.continuationStoppedAt;
|
|
122
|
+
delete next.continuationStoppedDetail;
|
|
121
123
|
next.lastStartedAt ??= nowSeconds();
|
|
122
124
|
}
|
|
123
125
|
if (tokenBudget === undefined)
|
|
@@ -175,12 +177,13 @@ export async function recordContinuationDelivered(ref, signature, expectedGoalId
|
|
|
175
177
|
const counted = options.countUnattended === false ? 0 : 1;
|
|
176
178
|
const next = {
|
|
177
179
|
...goal,
|
|
178
|
-
consecutiveContinuations: (goal.consecutiveContinuations ?? 0) + 1,
|
|
180
|
+
consecutiveContinuations: (goal.consecutiveContinuations ?? 0) + (options.countStreak === false ? 0 : 1),
|
|
179
181
|
unattendedContinuations: (goal.unattendedContinuations ?? 0) + counted,
|
|
180
182
|
noProgressContinuations: (goal.noProgressContinuations ?? 0) + counted,
|
|
181
183
|
lastContinuationSignature: signature,
|
|
182
184
|
};
|
|
183
185
|
delete next.continuationStoppedAt;
|
|
186
|
+
delete next.continuationStoppedDetail;
|
|
184
187
|
if (next.status === "active")
|
|
185
188
|
next.lastStartedAt ??= nowSeconds();
|
|
186
189
|
await held.write(next);
|
|
@@ -199,6 +202,7 @@ export async function resetContinuationStreak(ref, options = {}) {
|
|
|
199
202
|
next.noProgressContinuations = 0;
|
|
200
203
|
delete next.lastContinuationSignature;
|
|
201
204
|
delete next.continuationStoppedAt;
|
|
205
|
+
delete next.continuationStoppedDetail;
|
|
202
206
|
if (options.unattended === true && next.status === "active")
|
|
203
207
|
next.lastStartedAt ??= nowSeconds();
|
|
204
208
|
await held.write(next);
|
|
@@ -206,7 +210,7 @@ export async function resetContinuationStreak(ref, options = {}) {
|
|
|
206
210
|
});
|
|
207
211
|
}
|
|
208
212
|
/** Claims one denial under the same lock as input/reset, so evaluation retries cannot append another stop. */
|
|
209
|
-
export async function recordGoalContinuationStopped(ref, expected, at, blockedReason) {
|
|
213
|
+
export async function recordGoalContinuationStopped(ref, expected, at, blockedReason, detail) {
|
|
210
214
|
return withGoalFileLock(ref, async (held) => {
|
|
211
215
|
const current = await readGoalFile(ref);
|
|
212
216
|
if (current?.id !== expected.id ||
|
|
@@ -230,6 +234,9 @@ export async function recordGoalContinuationStopped(ref, expected, at, blockedRe
|
|
|
230
234
|
}
|
|
231
235
|
delete next.lastStartedAt;
|
|
232
236
|
next.continuationStoppedAt = at;
|
|
237
|
+
// The claim alone cannot rebuild the session entries; keep the denied payload with it so a later
|
|
238
|
+
// session start can re-append exactly the entries a crash swallowed (senpi#3015).
|
|
239
|
+
next.continuationStoppedDetail = detail;
|
|
233
240
|
await held.write(next);
|
|
234
241
|
return { goal: next, recorded: true };
|
|
235
242
|
});
|
|
@@ -8,6 +8,13 @@ export type GoalStoreRef = {
|
|
|
8
8
|
};
|
|
9
9
|
export type GoalAccountingMode = "active" | "activeOrBlocked" | "activeOrComplete";
|
|
10
10
|
export type GoalUpdateSource = "model" | "user";
|
|
11
|
+
/** Deny reason + counters captured with a stop claim, so a later session start can re-append the stop entries if the process died between the claim and the append (senpi#3015). */
|
|
12
|
+
export type GoalContinuationStoppedDetail = {
|
|
13
|
+
reason: "cap" | "stale" | "repetition" | "length-exhausted" | "no-progress" | "context-overflow";
|
|
14
|
+
consecutiveContinuations: number;
|
|
15
|
+
unattendedContinuations: number;
|
|
16
|
+
noProgressContinuations: number;
|
|
17
|
+
};
|
|
11
18
|
export type Goal = {
|
|
12
19
|
id: string;
|
|
13
20
|
threadId: string;
|
|
@@ -22,6 +29,8 @@ export type Goal = {
|
|
|
22
29
|
lastContinuationSignature?: string;
|
|
23
30
|
/** Durable claim for a stopped continuation; cleared by input, resume, or a delivered continuation. */
|
|
24
31
|
continuationStoppedAt?: number;
|
|
32
|
+
/** Stop-publication payload captured with the claim; cleared together with it. */
|
|
33
|
+
continuationStoppedDetail?: GoalContinuationStoppedDetail;
|
|
25
34
|
createdAt: number;
|
|
26
35
|
updatedAt: number;
|
|
27
36
|
lastStartedAt?: number;
|
|
@@ -5,6 +5,7 @@ import { readGoal } from "../goal/store.js";
|
|
|
5
5
|
import { goalStoreRef } from "../goal/store-ref.js";
|
|
6
6
|
import { buildHandoffBrief } from "./brief.js";
|
|
7
7
|
import { estimateHandoff, hintText } from "./estimate.js";
|
|
8
|
+
import { createReplacementRelay } from "./replacement-relay.js";
|
|
8
9
|
export const HANDOFF_HINT_ENTRY_TYPE = "handoff-hint";
|
|
9
10
|
export const HANDOFF_BRIEF_MESSAGE_TYPE = "handoff-brief";
|
|
10
11
|
export const HANDOFF_HINT_EVENT = "handoff_hint";
|
|
@@ -51,6 +52,7 @@ const readActiveGoal = (ctx) => readGoal(goalStoreRef(ctx.sessionManager, ctx.cw
|
|
|
51
52
|
export function createHandoffHintExtension(dependencies = {}) {
|
|
52
53
|
const now = dependencies.now ?? Date.now;
|
|
53
54
|
const goalOf = dependencies.readGoal ?? readActiveGoal;
|
|
55
|
+
const acceptances = createReplacementRelay();
|
|
54
56
|
return (pi) => {
|
|
55
57
|
let ctx;
|
|
56
58
|
let timer;
|
|
@@ -168,22 +170,21 @@ export function createHandoffHintExtension(dependencies = {}) {
|
|
|
168
170
|
previousSessionFile: current.sessionManager.getSessionFile(),
|
|
169
171
|
contextTokens: usage?.tokens ?? estimate?.contextTokens ?? 0,
|
|
170
172
|
});
|
|
171
|
-
const accepted = estimate === undefined ? undefined : hintEvent("accepted", current, estimate);
|
|
172
173
|
const sessionId = current.sessionManager.getSessionId();
|
|
174
|
+
const previousSessionFile = current.sessionManager.getSessionFile();
|
|
175
|
+
// The replacement's session_start emits it through the new generation's pi (senpi#4051).
|
|
176
|
+
if (estimate !== undefined)
|
|
177
|
+
acceptances.hold(previousSessionFile, hintEvent("accepted", current, estimate));
|
|
173
178
|
hide(current);
|
|
174
|
-
const
|
|
175
|
-
parentSession:
|
|
179
|
+
const switching = current.newSession({
|
|
180
|
+
parentSession: previousSessionFile,
|
|
176
181
|
withSession: async (next) => {
|
|
177
182
|
await next.sendMessage({ customType: HANDOFF_BRIEF_MESSAGE_TYPE, content: brief, display: true }, { triggerTurn: false });
|
|
178
183
|
},
|
|
179
184
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
if (accepted !== undefined)
|
|
186
|
-
pi.events?.emit(HANDOFF_HINT_EVENT, accepted);
|
|
185
|
+
const result = await switching.finally(() => acceptances.release(previousSessionFile));
|
|
186
|
+
if (result.cancelled && estimate !== undefined && current.sessionManager.getSessionId() === sessionId)
|
|
187
|
+
render(current, estimate);
|
|
187
188
|
}
|
|
188
189
|
pi.registerCommand(HANDOFF_COMMAND, {
|
|
189
190
|
description: "Start a fresh session seeded with a short handoff brief (`/handoff keep` hides the hint)",
|
|
@@ -207,8 +208,11 @@ export function createHandoffHintExtension(dependencies = {}) {
|
|
|
207
208
|
await startFresh(current);
|
|
208
209
|
},
|
|
209
210
|
});
|
|
210
|
-
pi.on("session_start", (
|
|
211
|
+
pi.on("session_start", (event, next) => {
|
|
211
212
|
ctx = next;
|
|
213
|
+
const accepted = event.reason === "new" ? acceptances.take(event.previousSessionFile) : undefined;
|
|
214
|
+
if (accepted !== undefined)
|
|
215
|
+
pi.events?.emit(HANDOFF_HINT_EVENT, accepted);
|
|
212
216
|
disarm();
|
|
213
217
|
visible = undefined;
|
|
214
218
|
shownThisCrossing = false;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carries a value from a session to the session that replaces it (senpi#4051).
|
|
3
|
+
*
|
|
4
|
+
* `newSession()` retires the old extension generation before the replacement starts, so a captured `pi` cannot emit
|
|
5
|
+
* after the switch, and emitting before it would report a switch that `session_before_switch` may still cancel. The
|
|
6
|
+
* builtin factory outlives both generations in one process, so the value waits here, keyed by the session being
|
|
7
|
+
* replaced, until the replacement's `session_start` takes it through its own live `pi`. The holder releases its key
|
|
8
|
+
* once `newSession()` settles, so a cancelled or failed switch delivers nothing.
|
|
9
|
+
*/
|
|
10
|
+
export interface ReplacementRelay<T> {
|
|
11
|
+
hold(previousSessionFile: string | undefined, value: T): void;
|
|
12
|
+
take(previousSessionFile: string | undefined): T | undefined;
|
|
13
|
+
release(previousSessionFile: string | undefined): void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createReplacementRelay<T>(): ReplacementRelay<T>;
|
|
16
|
+
//# sourceMappingURL=replacement-relay.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createReplacementRelay() {
|
|
2
|
+
const pending = new Map();
|
|
3
|
+
return {
|
|
4
|
+
hold: (previousSessionFile, value) => {
|
|
5
|
+
pending.set(previousSessionFile, value);
|
|
6
|
+
},
|
|
7
|
+
take: (previousSessionFile) => {
|
|
8
|
+
const value = pending.get(previousSessionFile);
|
|
9
|
+
pending.delete(previousSessionFile);
|
|
10
|
+
return value;
|
|
11
|
+
},
|
|
12
|
+
release: (previousSessionFile) => {
|
|
13
|
+
pending.delete(previousSessionFile);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=replacement-relay.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
|
+
import { resolvePromptSurface } from "../../../dynamic-prompt/build.js";
|
|
4
5
|
import { defineTool } from "../../types.js";
|
|
5
6
|
import { injectHtmlRenderBootstrap } from "./bootstrap.js";
|
|
6
7
|
import { inlineLocalImages } from "./images.js";
|
|
@@ -36,6 +37,13 @@ function assertWithinInputLimit(html) {
|
|
|
36
37
|
throw new Error(`The page is ${html.length} characters; show_html_page accepts at most ${MAX_HTML_CHARACTERS} characters.${advice}`);
|
|
37
38
|
}
|
|
38
39
|
const clampHeight = (height) => height === undefined ? undefined : Math.min(2000, Math.max(80, Math.round(height)));
|
|
40
|
+
/**
|
|
41
|
+
* Whether this session's host shows the page. The OmO desktop opens every session it renders with
|
|
42
|
+
* `open_session.promptSurface: "app"` and publishes a completed call from its details; every other
|
|
43
|
+
* surface (terminal, chat bridge, or none named) only gets the file. The result states which happened,
|
|
44
|
+
* because the model trusts it in later turns too (senpi#6273).
|
|
45
|
+
*/
|
|
46
|
+
const hostRendersPage = (ctx) => (ctx.getSystemPromptOptions?.().surface ?? resolvePromptSurface(process.env)) === "app";
|
|
39
47
|
export const showHtmlPageTool = defineTool({
|
|
40
48
|
name: "show_html_page",
|
|
41
49
|
label: "Show HTML Page",
|
|
@@ -46,8 +54,8 @@ export const showHtmlPageTool = defineTool({
|
|
|
46
54
|
"Local images written as absolute file paths " +
|
|
47
55
|
"are inlined automatically after a magic-byte check (a renamed non-image is refused); pass screenshots " +
|
|
48
56
|
"that way, never as base64 data: URIs, since the page is limited to 512000 characters before images are " +
|
|
49
|
-
"inlined. In the desktop thread
|
|
50
|
-
"
|
|
57
|
+
"inlined. In the OmO desktop thread the page renders inline above your reply; elsewhere it is only " +
|
|
58
|
+
"saved to a file and not displayed. The result says which happened.",
|
|
51
59
|
exposure: "search",
|
|
52
60
|
allowLazyActivation: true,
|
|
53
61
|
searchKeywords: ["html", "chart", "table", "diagram", "visualize", "render page", "mockup"],
|
|
@@ -71,7 +79,9 @@ export const showHtmlPageTool = defineTool({
|
|
|
71
79
|
content: [
|
|
72
80
|
{
|
|
73
81
|
type: "text",
|
|
74
|
-
text:
|
|
82
|
+
text: (hostRendersPage(ctx)
|
|
83
|
+
? `The page was shown to the reader inline above your reply. A copy is saved at ${path}. `
|
|
84
|
+
: `Wrote the page to ${path}. It was not displayed in this interface. `) +
|
|
75
85
|
(missing === undefined
|
|
76
86
|
? "Local images were inlined."
|
|
77
87
|
: `Some local images could not be read and were left as written: ${missing.join(", ")}.`),
|
|
@@ -12,7 +12,7 @@ How to get the image right the first time, and how to fix it fast when it is not
|
|
|
12
12
|
When image generation tooling is present in your tool set, pick the surface that actually exists right now:
|
|
13
13
|
|
|
14
14
|
- If a native `image_generation` server tool is available, use it. The provider runs generation server-side and returns the image in the response stream.
|
|
15
|
-
- Otherwise, call
|
|
15
|
+
- Otherwise, call `generate_image`. It is eval-only, like `bash`: when an `eval` tool exists it is not in your direct tool list, so run it inside an eval cell as `tool.generate_image({ prompt: "..." })` (js) or `tool.generate_image(prompt="...")` (py); `tool_schema("generate_image")` lists every parameter. Without an eval tool it is an ordinary direct tool. It sends your prompt and optional reference images to the configured OpenAI-compatible endpoint and saves the result as a png, jpeg, or webp file. The model, quality, size, background, and format controls below apply to `generate_image`, not to the native server tool.
|
|
16
16
|
|
|
17
17
|
Check your current tool set before choosing. Tool state can change mid-session (model switch, credential change), so trust the tools you can see over what this page said at startup. If both surfaces ever appear, prefer the native server tool. When the request is clear, generate directly instead of asking for confirmation; ask only when a required reference image is missing.
|
|
18
18
|
|
|
@@ -22,20 +22,10 @@ export const generateImageTool = defineTool({
|
|
|
22
22
|
label: "Generate Image",
|
|
23
23
|
description: "Generate or edit an image with OpenAI gpt-image-2.6 (Sunburst by default, the most capable; Flare when speed matters more than quality; an endpoint without 2.6 falls back to 2.5) and save it as a png, jpeg, or webp file, optionally with a transparent background. Pass reference_image_paths to edit or reference existing images. Generate directly when the request is clear instead of asking for confirmation. Returns the saved file paths.",
|
|
24
24
|
promptSnippet: "Generate or edit images from prompts and optional reference images, saving them as png/jpeg/webp files.",
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
searchKeywords: [
|
|
30
|
-
"generate image",
|
|
31
|
-
"image generation",
|
|
32
|
-
"edit image",
|
|
33
|
-
"create a picture",
|
|
34
|
-
"illustration",
|
|
35
|
-
"mockup",
|
|
36
|
-
"gpt-image",
|
|
37
|
-
"transparent png",
|
|
38
|
-
],
|
|
25
|
+
// Eval-only, like bash (senpi#6269): whenever an eval tool is registered the model never sees
|
|
26
|
+
// this ~1K-token schema and calls `tool.generate_image({ prompt: "..." })` inside a cell instead;
|
|
27
|
+
// without an eval tool it is an ordinary direct tool. The bundled imagegen skill explains the call.
|
|
28
|
+
exposure: "eval",
|
|
39
29
|
parameters: Params,
|
|
40
30
|
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
41
31
|
const size = params.size ?? "auto";
|