@toddzheng024/dscode-desktop 0.0.0-stage → 0.7.34
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/LICENSE +21 -0
- package/README.md +120 -2
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/bin/apply_patch +9 -0
- package/cordis.patch.yml +433 -0
- package/docs/CHANGELOG.md +649 -0
- package/docs/account-login.md +44 -0
- package/docs/auto-review.md +128 -0
- package/docs/browser-use.md +784 -0
- package/docs/computer-use.md +81 -0
- package/docs/custom-providers.md +88 -0
- package/docs/demo.md +158 -0
- package/docs/distribution.md +58 -0
- package/docs/dscode-ultra.md +75 -0
- package/docs/email.md +296 -0
- package/docs/exec.md +40 -0
- package/docs/hub-distribution.md +212 -0
- package/docs/memory.md +121 -0
- package/docs/opencode-go.md +61 -0
- package/docs/plugin-hub.md +68 -0
- package/docs/releases/0.2.0.md +28 -0
- package/docs/releases/0.3.0.md +21 -0
- package/docs/releases/0.4.0.md +20 -0
- package/docs/releases/0.5.0.md +19 -0
- package/docs/releases/0.6.0.md +20 -0
- package/docs/releases/0.7.0.md +22 -0
- package/docs/releases/0.7.1.md +20 -0
- package/docs/releases/0.7.10.md +10 -0
- package/docs/releases/0.7.11.md +17 -0
- package/docs/releases/0.7.12.md +11 -0
- package/docs/releases/0.7.13.md +12 -0
- package/docs/releases/0.7.14.md +11 -0
- package/docs/releases/0.7.15.md +11 -0
- package/docs/releases/0.7.16.md +10 -0
- package/docs/releases/0.7.17.md +12 -0
- package/docs/releases/0.7.18.md +13 -0
- package/docs/releases/0.7.19.md +8 -0
- package/docs/releases/0.7.2.md +19 -0
- package/docs/releases/0.7.20.md +10 -0
- package/docs/releases/0.7.21.md +9 -0
- package/docs/releases/0.7.22.md +11 -0
- package/docs/releases/0.7.23.md +14 -0
- package/docs/releases/0.7.24.md +11 -0
- package/docs/releases/0.7.25.md +8 -0
- package/docs/releases/0.7.26.md +18 -0
- package/docs/releases/0.7.27.md +8 -0
- package/docs/releases/0.7.28.md +22 -0
- package/docs/releases/0.7.29.md +12 -0
- package/docs/releases/0.7.3.md +23 -0
- package/docs/releases/0.7.30.md +19 -0
- package/docs/releases/0.7.31.md +14 -0
- package/docs/releases/0.7.32.md +14 -0
- package/docs/releases/0.7.33.md +37 -0
- package/docs/releases/0.7.34.md +24 -0
- package/docs/releases/0.7.4.md +16 -0
- package/docs/releases/0.7.5.md +18 -0
- package/docs/releases/0.7.6.md +29 -0
- package/docs/releases/0.7.7.md +36 -0
- package/docs/releases/0.7.8.md +14 -0
- package/docs/releases/0.7.9.md +15 -0
- package/docs/session-bridge.md +80 -0
- package/docs/session-cards.md +53 -0
- package/docs/session-communication.md +186 -0
- package/docs/session-metrics.md +41 -0
- package/docs/skills.md +81 -0
- package/docs/triggers.md +338 -0
- package/docs/tui-commands.md +204 -0
- package/extensions/browser/manifest.json +22 -0
- package/extensions/browser/popup.css +6 -0
- package/extensions/browser/popup.html +11 -0
- package/extensions/browser/popup.mjs +47 -0
- package/extensions/browser/protocol.mjs +168 -0
- package/extensions/browser/worker.mjs +94 -0
- package/icon.svg +7 -0
- package/locale/en.json +6 -0
- package/locale/zh-cn.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system/LICENSE +21 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.md +15 -0
- package/node_modules/@deepseek-ai/node-addon-system/README.zh.md +15 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/flock.d.ts +13 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/flock.js +51 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/index.d.ts +73 -0
- package/node_modules/@deepseek-ai/node-addon-system/lib/index.js +98 -0
- package/node_modules/@deepseek-ai/node-addon-system/package.json +45 -0
- package/node_modules/@deepseek-ai/node-addon-system/src/flock.c +164 -0
- package/node_modules/@deepseek-ai/node-addon-system/src/main.c +298 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/README.zh.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/bin/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-arm64/prebuilds.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/README.zh.md +9 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/bin/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-darwin-x64/prebuilds.json +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/README.zh.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/glibc/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/landlock-run +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/bin/musl/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-arm64/prebuilds.json +24 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/LICENSE +28 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.i18n.yaml +6 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/README.zh.md +11 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/glibc/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/landlock-run +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/bin/musl/system.node +0 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/package.json +31 -0
- package/node_modules/@deepseek-ai/node-addon-system-linux-x64/prebuilds.json +24 -0
- package/package.json +156 -4
- package/plugins/account/index.mjs +178 -0
- package/plugins/account/state.mjs +123 -0
- package/plugins/auto-review/audit.mjs +24 -0
- package/plugins/auto-review/index.mjs +267 -0
- package/plugins/auto-review/policy.mjs +92 -0
- package/plugins/browser/SKILL.md +158 -0
- package/plugins/browser/access.mjs +143 -0
- package/plugins/browser/config.mjs +60 -0
- package/plugins/browser/connection.mjs +385 -0
- package/plugins/browser/desktop-client.mjs +282 -0
- package/plugins/browser/desktop-host.mjs +74 -0
- package/plugins/browser/document-identity.mjs +57 -0
- package/plugins/browser/extension-relay.mjs +66 -0
- package/plugins/browser/files.mjs +32 -0
- package/plugins/browser/index.mjs +235 -0
- package/plugins/browser/mcp-entry.mjs +18 -0
- package/plugins/browser/mcp-tools.mjs +21 -0
- package/plugins/browser/presentation.mjs +45 -0
- package/plugins/browser/preview.mjs +77 -0
- package/plugins/browser/review.mjs +20 -0
- package/plugins/browser/screenshots.mjs +35 -0
- package/plugins/clipboard-image/clipboard-image.swift +56 -0
- package/plugins/clipboard-image/index.mjs +70 -0
- package/plugins/code-review/baseline.mjs +143 -0
- package/plugins/code-review/git.mjs +163 -0
- package/plugins/code-review/index.mjs +236 -0
- package/plugins/compaction/engine.mjs +304 -0
- package/plugins/compaction/tetris.mjs +65 -0
- package/plugins/compaction/threshold.mjs +99 -0
- package/plugins/computer-use/DESKTOP-SKILL.md +64 -0
- package/plugins/computer-use/desktop-host.mjs +75 -0
- package/plugins/computer-use/desktop-skill.mjs +28 -0
- package/plugins/computer-use/desktop-upstream.mjs +3 -0
- package/plugins/computer-use/index.mjs +37 -0
- package/plugins/cordis-config/plain.mjs +31 -0
- package/plugins/credentials/index.mjs +73 -0
- package/plugins/custom/adapter.mjs +110 -0
- package/plugins/custom/config.mjs +85 -0
- package/plugins/custom/desktop-client.mjs +150 -0
- package/plugins/custom/desktop-host.mjs +78 -0
- package/plugins/custom/images.mjs +32 -0
- package/plugins/custom/index.mjs +145 -0
- package/plugins/custom/wire.mjs +179 -0
- package/plugins/desktop/client.mjs +1041 -0
- package/plugins/desktop/index.mjs +10 -0
- package/plugins/desktop/shell-environment.mjs +9 -0
- package/plugins/desktop-workspace/hooks.mjs +29 -0
- package/plugins/desktop-workspace/index.mjs +37 -0
- package/plugins/dscode/board.mjs +260 -0
- package/plugins/dscode/child-target.mjs +11 -0
- package/plugins/dscode/control.mjs +17 -0
- package/plugins/dscode/desktop-client.mjs +55 -0
- package/plugins/dscode/desktop-host.mjs +37 -0
- package/plugins/dscode/index.mjs +270 -0
- package/plugins/email/cli.mjs +32 -0
- package/plugins/email/contacts.mjs +36 -0
- package/plugins/email/desktop-client.mjs +117 -0
- package/plugins/email/desktop-host.mjs +54 -0
- package/plugins/email/desktop-service.mjs +106 -0
- package/plugins/email/gmail-oauth.mjs +69 -0
- package/plugins/email/gmail-store.mjs +2 -0
- package/plugins/email/gmail.mjs +194 -0
- package/plugins/email/imap.mjs +136 -0
- package/plugins/email/inbox.d.mts +25 -0
- package/plugins/email/inbox.mjs +76 -0
- package/plugins/email/smtp.mjs +70 -0
- package/plugins/email/store.mjs +32 -0
- package/plugins/email-tools/index.mjs +46 -0
- package/plugins/exec/cli.mjs +64 -0
- package/plugins/exec/index.mjs +112 -0
- package/plugins/grok/adapter.mjs +114 -0
- package/plugins/grok/auth.mjs +76 -0
- package/plugins/grok/billing.mjs +147 -0
- package/plugins/grok/index.mjs +123 -0
- package/plugins/grok/models.mjs +129 -0
- package/plugins/grok/status.mjs +29 -0
- package/plugins/grok/wire.mjs +268 -0
- package/plugins/hub/catalog.mjs +82 -0
- package/plugins/hub/desktop-client.mjs +114 -0
- package/plugins/hub/desktop-host.mjs +129 -0
- package/plugins/i18n/messages.d.mts +21 -0
- package/plugins/i18n/messages.mjs +537 -0
- package/plugins/jev/approval.mjs +131 -0
- package/plugins/jev/client.mjs +74 -0
- package/plugins/jev/index.mjs +100 -0
- package/plugins/memory/content.mjs +57 -0
- package/plugins/memory/desktop-host.mjs +3 -0
- package/plugins/memory/index.mjs +168 -0
- package/plugins/memory/pipeline.mjs +79 -0
- package/plugins/memory/store.mjs +93 -0
- package/plugins/message-source/kind.mjs +20 -0
- package/plugins/opencode-go/adapter.mjs +96 -0
- package/plugins/opencode-go/command.mjs +128 -0
- package/plugins/opencode-go/index.mjs +121 -0
- package/plugins/opencode-go/models.mjs +58 -0
- package/plugins/opencode-go/oauth.mjs +194 -0
- package/plugins/opencode-go/search.mjs +86 -0
- package/plugins/opencode-go/usage.mjs +120 -0
- package/plugins/opencode-go/version.mjs +10 -0
- package/plugins/opencode-go/wire.mjs +73 -0
- package/plugins/openrouter/adapter.mjs +232 -0
- package/plugins/openrouter/index.mjs +124 -0
- package/plugins/openrouter/models.mjs +157 -0
- package/plugins/openrouter/search.mjs +113 -0
- package/plugins/openrouter/wire.mjs +101 -0
- package/plugins/providers/catalog.mjs +160 -0
- package/plugins/providers/chat-messages.mjs +120 -0
- package/plugins/providers/chat-stream.mjs +174 -0
- package/plugins/providers/desktop-client.mjs +84 -0
- package/plugins/providers/desktop-host.mjs +63 -0
- package/plugins/providers/effort.mjs +35 -0
- package/plugins/providers/http-errors.mjs +50 -0
- package/plugins/providers/openrouter-account.mjs +171 -0
- package/plugins/session-bridge/client.mjs +106 -0
- package/plugins/session-bridge/communication.mjs +260 -0
- package/plugins/session-bridge/index.mjs +67 -0
- package/plugins/session-bridge/mailbox.mjs +267 -0
- package/plugins/session-bridge/paths.mjs +21 -0
- package/plugins/session-bridge/server.mjs +172 -0
- package/plugins/session-bridge/tasks.mjs +229 -0
- package/plugins/session-cards/content.mjs +64 -0
- package/plugins/session-cards/index.mjs +38 -0
- package/plugins/session-cards/manager.mjs +140 -0
- package/plugins/session-metrics/attribution.mjs +30 -0
- package/plugins/session-metrics/balance.mjs +81 -0
- package/plugins/session-metrics/desktop-client.mjs +62 -0
- package/plugins/session-metrics/desktop-host.mjs +46 -0
- package/plugins/session-metrics/index.mjs +97 -0
- package/plugins/session-metrics/pricing.mjs +84 -0
- package/plugins/session-metrics/rate.mjs +59 -0
- package/plugins/session-metrics/store.mjs +75 -0
- package/plugins/session-metrics/turns.mjs +134 -0
- package/plugins/session-metrics/view.mjs +272 -0
- package/plugins/time-marks/desktop-host.mjs +2 -0
- package/plugins/time-marks/index.mjs +116 -0
- package/plugins/time-marks/marks.mjs +152 -0
- package/plugins/triggers/cli.mjs +558 -0
- package/plugins/triggers/commands.mjs +143 -0
- package/plugins/triggers/config.mjs +264 -0
- package/plugins/triggers/desktop-client.mjs +155 -0
- package/plugins/triggers/desktop-host.mjs +88 -0
- package/plugins/triggers/desktop-management.mjs +97 -0
- package/plugins/triggers/desktop-scheduler.mjs +125 -0
- package/plugins/triggers/host.mjs +24 -0
- package/plugins/triggers/index.mjs +27 -0
- package/plugins/triggers/job-cli.mjs +110 -0
- package/plugins/triggers/jobs.mjs +156 -0
- package/plugins/triggers/launchd.mjs +101 -0
- package/plugins/triggers/lease.mjs +24 -0
- package/plugins/triggers/log.mjs +104 -0
- package/plugins/triggers/management.mjs +181 -0
- package/plugins/triggers/options.mjs +117 -0
- package/plugins/triggers/overlay.mjs +7 -0
- package/plugins/triggers/poll.mjs +65 -0
- package/plugins/triggers/run.mjs +157 -0
- package/plugins/triggers/schedule.mjs +40 -0
- package/plugins/triggers/scheduler-service.mjs +46 -0
- package/plugins/triggers/scheduler.mjs +92 -0
- package/plugins/triggers/session-run.mjs +125 -0
- package/plugins/triggers/session.mjs +48 -0
- package/plugins/triggers/source-emit.mjs +19 -0
- package/plugins/triggers/source-host.mjs +78 -0
- package/plugins/triggers/source-ingress.mjs +70 -0
- package/plugins/triggers/source-sandbox.mjs +35 -0
- package/plugins/triggers/sources.mjs +45 -0
- package/plugins/triggers/spool.mjs +145 -0
- package/plugins/triggers/tools.mjs +67 -0
- package/plugins/triggers/unattended.mjs +18 -0
- package/plugins/tui-tools/composition.mjs +28 -0
- package/plugins/tui-tools/desktop-host.mjs +2 -0
- package/plugins/tui-tools/doctor-cli.mjs +22 -0
- package/plugins/tui-tools/doctor.mjs +149 -0
- package/plugins/tui-tools/hook-sources.mjs +113 -0
- package/plugins/tui-tools/hooks.mjs +23 -0
- package/plugins/tui-tools/index.mjs +269 -0
- package/plugins/tui-tools/sandbox-runner.mjs +123 -0
- package/plugins/tui-tools/shell.mjs +27 -0
- package/plugins/tui-tools/update.mjs +111 -0
- package/plugins/tui-tools/workspace-discovery.mjs +129 -0
- package/plugins/ultra/policy.mjs +42 -0
- package/plugins/worktree-subagent/worktree.mjs +56 -0
- package/runtime-sources.json +94 -0
- package/vendor/bash/LICENSE +21 -0
- package/vendor/bash/index.js +717 -0
- package/vendor/bash/types/background.d.ts +50 -0
- package/vendor/bash/types/index.d.ts +38 -0
- package/vendor/bash/types/render.d.ts +57 -0
- package/vendor/command-goal/LICENSE +21 -0
- package/vendor/command-goal/index.js +210 -0
- package/vendor/command-goal/types/index.d.ts +10 -0
- package/vendor/computer-use/LICENSE +21 -0
- package/vendor/computer-use/lib/approval-policy.js +12 -0
- package/vendor/computer-use/lib/approval-policy.js.map +1 -0
- package/vendor/computer-use/lib/artifacts.js +79 -0
- package/vendor/computer-use/lib/artifacts.js.map +1 -0
- package/vendor/computer-use/lib/backend.js +3 -0
- package/vendor/computer-use/lib/backend.js.map +1 -0
- package/vendor/computer-use/lib/client.js +369 -0
- package/vendor/computer-use/lib/client.js.map +1 -0
- package/vendor/computer-use/lib/config.js +159 -0
- package/vendor/computer-use/lib/config.js.map +1 -0
- package/vendor/computer-use/lib/confirmations.js +99 -0
- package/vendor/computer-use/lib/confirmations.js.map +1 -0
- package/vendor/computer-use/lib/diff.js +58 -0
- package/vendor/computer-use/lib/diff.js.map +1 -0
- package/vendor/computer-use/lib/errors.js +22 -0
- package/vendor/computer-use/lib/errors.js.map +1 -0
- package/vendor/computer-use/lib/exposure.js +244 -0
- package/vendor/computer-use/lib/exposure.js.map +1 -0
- package/vendor/computer-use/lib/index.js +66 -0
- package/vendor/computer-use/lib/index.js.map +1 -0
- package/vendor/computer-use/lib/leases.js +262 -0
- package/vendor/computer-use/lib/leases.js.map +1 -0
- package/vendor/computer-use/lib/providers/macos.js +243 -0
- package/vendor/computer-use/lib/providers/macos.js.map +1 -0
- package/vendor/computer-use/lib/providers/native-helper.js +726 -0
- package/vendor/computer-use/lib/providers/native-helper.js.map +1 -0
- package/vendor/computer-use/lib/providers/unsupported.js +51 -0
- package/vendor/computer-use/lib/providers/unsupported.js.map +1 -0
- package/vendor/computer-use/lib/service.js +608 -0
- package/vendor/computer-use/lib/service.js.map +1 -0
- package/vendor/computer-use/lib/skill.js +136 -0
- package/vendor/computer-use/lib/skill.js.map +1 -0
- package/vendor/computer-use/lib/target-resolver.js +180 -0
- package/vendor/computer-use/lib/target-resolver.js.map +1 -0
- package/vendor/computer-use/lib/tools.js +511 -0
- package/vendor/computer-use/lib/tools.js.map +1 -0
- package/vendor/computer-use/lib/types/approval-policy.d.ts +13 -0
- package/vendor/computer-use/lib/types/approval-policy.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/artifacts.d.ts +10 -0
- package/vendor/computer-use/lib/types/artifacts.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/backend.d.ts +124 -0
- package/vendor/computer-use/lib/types/backend.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/client/index.d.ts +83 -0
- package/vendor/computer-use/lib/types/client/index.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/config.d.ts +94 -0
- package/vendor/computer-use/lib/types/config.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/confirmations.d.ts +23 -0
- package/vendor/computer-use/lib/types/confirmations.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/diff.d.ts +5 -0
- package/vendor/computer-use/lib/types/diff.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/errors.d.ts +15 -0
- package/vendor/computer-use/lib/types/errors.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/exposure.d.ts +30 -0
- package/vendor/computer-use/lib/types/exposure.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/index.d.ts +21 -0
- package/vendor/computer-use/lib/types/index.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/leases.d.ts +81 -0
- package/vendor/computer-use/lib/types/leases.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/macos.d.ts +39 -0
- package/vendor/computer-use/lib/types/providers/macos.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/native-helper.d.ts +48 -0
- package/vendor/computer-use/lib/types/providers/native-helper.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/providers/unsupported.d.ts +23 -0
- package/vendor/computer-use/lib/types/providers/unsupported.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/service.d.ts +58 -0
- package/vendor/computer-use/lib/types/service.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/skill.d.ts +14 -0
- package/vendor/computer-use/lib/types/skill.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/target-resolver.d.ts +38 -0
- package/vendor/computer-use/lib/types/target-resolver.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/tools.d.ts +6 -0
- package/vendor/computer-use/lib/types/tools.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/types.d.ts +270 -0
- package/vendor/computer-use/lib/types/types.d.ts.map +1 -0
- package/vendor/computer-use/lib/types/web.d.ts +33 -0
- package/vendor/computer-use/lib/types/web.d.ts.map +1 -0
- package/vendor/computer-use/lib/types.js +9 -0
- package/vendor/computer-use/lib/types.js.map +1 -0
- package/vendor/computer-use/lib/web.js +188 -0
- package/vendor/computer-use/lib/web.js.map +1 -0
- package/vendor/computer-use/native/macos/Sources/Fixture/main.swift +412 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorImage.swift +11 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorMotion.swift +67 -0
- package/vendor/computer-use/native/macos/Sources/Helper/CursorOverlay.swift +840 -0
- package/vendor/computer-use/native/macos/Sources/Helper/TargetedPointer.swift +327 -0
- package/vendor/computer-use/native/macos/Sources/Helper/WindowNumberMatcher.swift +29 -0
- package/vendor/computer-use/native/macos/Sources/Helper/main.swift +1244 -0
- package/vendor/computer-use/native/macos/Sources/Monitor/main.swift +153 -0
- package/vendor/computer-use/native/macos/bin/dsh-computer-use-helper +0 -0
- package/vendor/computer-use/native/macos/manifest.json +14 -0
- package/vendor/persistent/LICENSE +21 -0
- package/vendor/persistent/index.js +411 -0
- package/vendor/persistent/types/index.d.ts +24 -0
- package/vendor/subagent/LICENSE +21 -0
- package/vendor/subagent/index.js +694 -0
- package/vendor/subagent/invariant.js +52 -0
- package/vendor/subagent/model-selection-settings.js +68 -0
- package/vendor/subagent/types/index.d.ts +82 -0
- package/vendor/subagent/types/invariant.d.ts +16 -0
- package/vendor/subagent/types/list-models.d.ts +10 -0
- package/vendor/subagent/types/model-selection-settings.d.ts +45 -0
- package/vendor/subagent/types/model-selection-state.d.ts +48 -0
- package/vendor/subagent/types/model-selection.d.ts +81 -0
- package/vendor/subagent-core/LICENSE +21 -0
- package/vendor/subagent-core/index.js +3202 -0
- package/vendor/subagent-core/invariant.js +76 -0
- package/vendor/subagent-core/typert.host.d.ts +3 -0
- package/vendor/subagent-core/typert.host.js +978 -0
- package/vendor/subagent-core/typert.remote-client.d.ts +25 -0
- package/vendor/subagent-core/typert.remote-client.js +114 -0
- package/vendor/subagent-core/types/archive-admission.d.ts +16 -0
- package/vendor/subagent-core/types/archive-admission.js +151 -0
- package/vendor/subagent-core/types/assistant-output.d.ts +48 -0
- package/vendor/subagent-core/types/assistant-output.js +73 -0
- package/vendor/subagent-core/types/catalog.d.ts +84 -0
- package/vendor/subagent-core/types/catalog.js +109 -0
- package/vendor/subagent-core/types/child-agent.d.ts +156 -0
- package/vendor/subagent-core/types/child-agent.js +213 -0
- package/vendor/subagent-core/types/client.d.ts +8 -0
- package/vendor/subagent-core/types/client.js +7 -0
- package/vendor/subagent-core/types/continuation-activation.d.ts +267 -0
- package/vendor/subagent-core/types/continuation-activation.js +700 -0
- package/vendor/subagent-core/types/continuation-messages.d.ts +62 -0
- package/vendor/subagent-core/types/continuation-messages.js +106 -0
- package/vendor/subagent-core/types/continuation.d.ts +124 -0
- package/vendor/subagent-core/types/continuation.js +455 -0
- package/vendor/subagent-core/types/control-types.d.ts +135 -0
- package/vendor/subagent-core/types/control-types.js +8 -0
- package/vendor/subagent-core/types/control.d.ts +45 -0
- package/vendor/subagent-core/types/control.js +79 -0
- package/vendor/subagent-core/types/depth.d.ts +31 -0
- package/vendor/subagent-core/types/depth.js +39 -0
- package/vendor/subagent-core/types/descriptor.d.ts +144 -0
- package/vendor/subagent-core/types/descriptor.js +193 -0
- package/vendor/subagent-core/types/error.d.ts +11 -0
- package/vendor/subagent-core/types/error.js +14 -0
- package/vendor/subagent-core/types/inbox.d.ts +43 -0
- package/vendor/subagent-core/types/inbox.js +61 -0
- package/vendor/subagent-core/types/index.d.ts +320 -0
- package/vendor/subagent-core/types/index.js +475 -0
- package/vendor/subagent-core/types/internal.d.ts +58 -0
- package/vendor/subagent-core/types/internal.js +58 -0
- package/vendor/subagent-core/types/invariant.d.ts +13 -0
- package/vendor/subagent-core/types/invariant.js +91 -0
- package/vendor/subagent-core/types/lifecycle.d.ts +113 -0
- package/vendor/subagent-core/types/lifecycle.js +180 -0
- package/vendor/subagent-core/types/list-children.d.ts +36 -0
- package/vendor/subagent-core/types/list-children.js +166 -0
- package/vendor/subagent-core/types/out-of-process.d.ts +118 -0
- package/vendor/subagent-core/types/out-of-process.js +219 -0
- package/vendor/subagent-core/types/projection-types.d.ts +82 -0
- package/vendor/subagent-core/types/projection-types.js +7 -0
- package/vendor/subagent-core/types/projection.d.ts +100 -0
- package/vendor/subagent-core/types/projection.js +157 -0
- package/vendor/subagent-core/types/run-settlement.d.ts +17 -0
- package/vendor/subagent-core/types/run-settlement.js +70 -0
- package/vendor/subagent-core/types/types.d.ts +377 -0
- package/vendor/subagent-core/types/types.js +19 -0
- package/vendor/subagent-driver/LICENSE +21 -0
- package/vendor/subagent-driver/index.js +255 -0
- package/vendor/subagent-driver/types/index.d.ts +33 -0
- package/vendor/subagent-driver/types/structured.d.ts +42 -0
- package/vendor/subagent-fork/LICENSE +21 -0
- package/vendor/subagent-fork/index.js +61 -0
- package/vendor/subagent-fork/types/index.d.ts +20 -0
- package/vendor/subagent-spawn/LICENSE +21 -0
- package/vendor/subagent-spawn/index.js +45 -0
- package/vendor/subagent-spawn/types/index.d.ts +19 -0
- package/vendor/terminal/LICENSE +21 -0
- package/vendor/terminal/index.js +1093 -0
- package/vendor/terminal/types/config.d.ts +82 -0
- package/vendor/terminal/types/index.d.ts +36 -0
- package/vendor/terminal/types/sanitize.d.ts +47 -0
- package/vendor/terminal/types/session.d.ts +87 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Per-turn cost attribution and the session budget gate. The footer reports the
|
|
2
|
+
// whole session's spend and /usage reports tokens per turn; these functions
|
|
3
|
+
// answer what neither does with that ledger: which turn spent the money, so the
|
|
4
|
+
// footer can show the last completed turn and /usage can price each one.
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Turn windows from the session's own events. `turn/start` opens a window and
|
|
8
|
+
* `turn/end` closes it; an unterminated turn stays open to `Infinity` so the
|
|
9
|
+
* turn in flight still attributes its settled calls. Windows come back in
|
|
10
|
+
* ascending start order, which the attribution lookup below depends on.
|
|
11
|
+
*/
|
|
12
|
+
export function turnWindows(events = []) {
|
|
13
|
+
const open = new Map();
|
|
14
|
+
const windows = [];
|
|
15
|
+
for (const event of events) {
|
|
16
|
+
const turn = event.data?.turn;
|
|
17
|
+
// A boundary without a time cannot place a window or a call inside one. It
|
|
18
|
+
// is skipped rather than stored: a NaN edge would make the ascending-search
|
|
19
|
+
// contract false and silently orphan every call of that turn.
|
|
20
|
+
if (!Number.isFinite(turn) || !Number.isFinite(event.time)) continue;
|
|
21
|
+
if (event.type === 'turn/start') {
|
|
22
|
+
const existing = open.get(turn);
|
|
23
|
+
// A resumed turn can re-emit its start; keep the earliest boundary.
|
|
24
|
+
open.set(turn, existing === undefined ? event.time : Math.min(existing, event.time));
|
|
25
|
+
} else if (event.type === 'turn/end') {
|
|
26
|
+
const start = open.get(turn);
|
|
27
|
+
if (start === undefined) continue;
|
|
28
|
+
open.delete(turn);
|
|
29
|
+
windows.push({ turn, start, end: event.time });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
for (const [turn, start] of open) windows.push({ turn, start, end: Infinity });
|
|
33
|
+
return windows.sort((left, right) => left.start - right.start || left.turn - right.turn);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The clauses a call is priced against: each turn's window, with an open turn
|
|
38
|
+
* (no `turn/end`) bounded by the next turn's start. An interrupted turn must not
|
|
39
|
+
* swallow every later turn, while the turn genuinely in flight keeps running to
|
|
40
|
+
* `Infinity`. Satisfies {@link attributeCostByTurn}'s ascending-start contract.
|
|
41
|
+
*/
|
|
42
|
+
export function turnBounds(events = []) {
|
|
43
|
+
return turnWindows(events).map((window, index, windows) => ({
|
|
44
|
+
turn: window.turn,
|
|
45
|
+
start: window.start,
|
|
46
|
+
// Math.max keeps the window non-inverted even for a log whose timestamps
|
|
47
|
+
// are out of order; an inverted bound would break the ascending search.
|
|
48
|
+
end: window.end === Infinity && index + 1 < windows.length ? Math.max(window.start, windows[index + 1].start) : window.end,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The window containing `time`, by binary search over the ascending bounds. The
|
|
54
|
+
* index is the FIRST window starting after `time`, so the one before it is the
|
|
55
|
+
* only candidate that can contain it; `-1` means no turn was running.
|
|
56
|
+
* @param bounds - ascending {@link turnBounds} rows.
|
|
57
|
+
* @param time - the call's start time (`time` prices the call, so it is also when the turn ran).
|
|
58
|
+
* @returns the bounds index, or -1 when `time` falls in no window.
|
|
59
|
+
*/
|
|
60
|
+
export function turnAt(bounds, time) {
|
|
61
|
+
let low = 0, high = bounds.length;
|
|
62
|
+
while (low < high) {
|
|
63
|
+
const mid = (low + high) >> 1;
|
|
64
|
+
if (bounds[mid].start <= time) low = mid + 1;
|
|
65
|
+
else high = mid;
|
|
66
|
+
}
|
|
67
|
+
const candidate = bounds[low - 1];
|
|
68
|
+
return candidate !== undefined && time <= candidate.end ? low - 1 : -1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Cost and call count per turn. Roughly O(calls × log turns), so the footer can
|
|
73
|
+
* ask for the last turn once a second. A call is attributed by its start time,
|
|
74
|
+
* and a call outside every window (before the first `turn/start`, in a gap
|
|
75
|
+
* between two turns, or a backfilled row without one) is reported under
|
|
76
|
+
* `unattributed` rather than dropped.
|
|
77
|
+
* @param rows - ledger rows (`{ kind, time, cost, purpose }`).
|
|
78
|
+
* @param events - the session's events, for the turn windows.
|
|
79
|
+
* @returns One bucket per turn, plus `lastTurn` (the newest turn that is no
|
|
80
|
+
* longer running, undefined while none has stopped), the `unattributed` total
|
|
81
|
+
* and whether any of it was unpriceable. A turn that never emitted `turn/end`
|
|
82
|
+
* still counts as stopped once the next turn opens its window.
|
|
83
|
+
*/
|
|
84
|
+
export function attributeCostByTurn(rows = [], events = []) {
|
|
85
|
+
const bounds = turnBounds(events);
|
|
86
|
+
const turns = bounds.map(bound => ({ turn: bound.turn, cost: 0, calls: 0, unknown: false }));
|
|
87
|
+
let unattributed = 0;
|
|
88
|
+
let unattributedUnknown = false;
|
|
89
|
+
for (const row of rows ?? []) {
|
|
90
|
+
if (row?.kind !== 'end') continue;
|
|
91
|
+
const at = turnAt(bounds, row.time);
|
|
92
|
+
if (at === -1) {
|
|
93
|
+
// Outside every window the spend still exists: an unpriceable row must be
|
|
94
|
+
// visible as such rather than counted as nothing.
|
|
95
|
+
if (Number.isFinite(row.cost)) unattributed += row.cost;
|
|
96
|
+
else unattributedUnknown = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
turns[at].calls += 1;
|
|
100
|
+
if (Number.isFinite(row.cost)) turns[at].cost += row.cost;
|
|
101
|
+
else turns[at].unknown = true;
|
|
102
|
+
}
|
|
103
|
+
let lastTurn;
|
|
104
|
+
for (let index = bounds.length - 1; index >= 0; index -= 1) {
|
|
105
|
+
if (bounds[index].end !== Infinity) { lastTurn = turns[index]; break; }
|
|
106
|
+
}
|
|
107
|
+
return { turns, lastTurn, unattributed, unattributedUnknown };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The budget gate's verdict. A missing or non-positive limit means no budget is
|
|
112
|
+
* set, so the gate never fires and the footer shows no budget slot. `warn` is
|
|
113
|
+
* the alerting share; `over` means the next prompt would spend past the limit.
|
|
114
|
+
*/
|
|
115
|
+
export function evaluateBudget(spent, limit, options = {}) {
|
|
116
|
+
const warnPercent = Number.isFinite(options.warnPercent) ? options.warnPercent : 80
|
|
117
|
+
if (!Number.isFinite(limit) || limit <= 0) return { state: 'unset', limit: null, spent, ratio: null, percent: null, warnPercent }
|
|
118
|
+
const safeSpent = Number.isFinite(spent) ? spent : 0
|
|
119
|
+
const ratio = safeSpent / limit
|
|
120
|
+
return {
|
|
121
|
+
state: ratio >= 1 ? 'over' : ratio * 100 >= warnPercent ? 'warn' : 'ok',
|
|
122
|
+
limit, spent: safeSpent, ratio,
|
|
123
|
+
percent: Math.round(ratio * 100),
|
|
124
|
+
warnPercent,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Parse the `DSCODE_SESSION_BUDGET_USD`-style value; anything else is no budget. */
|
|
129
|
+
export function parseBudget(value) {
|
|
130
|
+
if (typeof value === 'number') return Number.isFinite(value) && value > 0 ? value : null
|
|
131
|
+
if (typeof value !== 'string') return null
|
|
132
|
+
const parsed = Number.parseFloat(value.trim())
|
|
133
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null
|
|
134
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
2
|
+
import { readMetrics } from './store.mjs';
|
|
3
|
+
import { t } from '../i18n/messages.mjs';
|
|
4
|
+
import { estimateCost, peakEmoji } from './pricing.mjs';
|
|
5
|
+
import { balanceNow, trustedNow } from './balance.mjs';
|
|
6
|
+
import { grokSubscriptionNow } from '../grok/billing.mjs';
|
|
7
|
+
import { goUsageNow } from '../opencode-go/usage.mjs';
|
|
8
|
+
import { canonicalProvider } from '../providers/catalog.mjs';
|
|
9
|
+
import { sessionAverageTps } from './rate.mjs';
|
|
10
|
+
import { attributeCostByTurn, evaluateBudget, parseBudget } from './turns.mjs';
|
|
11
|
+
let source;
|
|
12
|
+
export function setMetricSource(next) { source = next; return () => { if (source === next) source = undefined; }; }
|
|
13
|
+
export function summarize(rows, events = [], corrupt = false) {
|
|
14
|
+
const requests = new Map();
|
|
15
|
+
let first = Infinity;
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
if (row.kind === 'start') first = Math.min(first, row.time);
|
|
18
|
+
requests.set(row.id, { ...requests.get(row.id), ...row });
|
|
19
|
+
}
|
|
20
|
+
// Backfill old main/compaction calls from the durable log, only before the new ledger.
|
|
21
|
+
let route = {};
|
|
22
|
+
const history = [];
|
|
23
|
+
for (const event of events) {
|
|
24
|
+
if (event.type === 'request/header') route = event.data.header.config;
|
|
25
|
+
// Ledger rows cover everything from the first row's time on, so only older events are
|
|
26
|
+
// backfilled. `continue`, not `break`: the loop must not depend on event ordering.
|
|
27
|
+
if (event.time >= first) continue;
|
|
28
|
+
if (event.type === 'assistant/message' || event.type === 'compaction/summary' && event.data.llmStreamCall) {
|
|
29
|
+
const r = event.type === 'compaction/summary' ? event.data : route;
|
|
30
|
+
history.push({ usage: event.data.usage, cost: estimateCost(r.provider, r.model, event.data.usage, event.time), kind: 'end' });
|
|
31
|
+
} else if (event.type === 'assistant/attempt') history.push({ kind: 'end', cost: null });
|
|
32
|
+
}
|
|
33
|
+
let cost = 0, unknown = corrupt || history.length > 0, input = 0, hit = 0, cacheUnknown = false, calls = 0, pending = 0;
|
|
34
|
+
// Historical overhead (review/title) was not recorded by this collector: mark partial.
|
|
35
|
+
for (const row of [...history, ...requests.values()]) {
|
|
36
|
+
calls++;
|
|
37
|
+
if (row.kind !== 'end') { pending++; continue; }
|
|
38
|
+
if (!Number.isFinite(row.cost)) unknown = true;
|
|
39
|
+
else cost += row.cost;
|
|
40
|
+
const u = row.usage;
|
|
41
|
+
// An auxiliary call (memory, review, an aborted stream) can end without a usage chunk: that
|
|
42
|
+
// call is unaccounted, not a cache miss, so it must not blank the whole session's ratio.
|
|
43
|
+
if (!u) continue;
|
|
44
|
+
if (!Number.isFinite(u.inputTokens) || !Number.isFinite(u.outputTokens)) { cacheUnknown = true; continue; }
|
|
45
|
+
const total = u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
|
|
46
|
+
// OpenRouter reports cache reads only when there are some: a missing count is zero, not unknown.
|
|
47
|
+
input += total; hit += u.cacheReadTokens ?? 0;
|
|
48
|
+
}
|
|
49
|
+
// Per-turn attribution needs the ledger's own rows (the backfilled history has
|
|
50
|
+
// no turn boundary), so it reads them directly instead of the merged map.
|
|
51
|
+
const attribution = attributeCostByTurn(rows, events);
|
|
52
|
+
return {
|
|
53
|
+
cost, unknown, calls, pending,
|
|
54
|
+
cache: input > 0 && !cacheUnknown ? Math.min(100, hit / input * 100) : null,
|
|
55
|
+
turns: attribution.turns,
|
|
56
|
+
lastTurn: attribution.lastTurn,
|
|
57
|
+
unattributed: attribution.unattributed,
|
|
58
|
+
unattributedUnknown: attribution.unattributedUnknown,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Per-turn cost for one live session, straight from its ledger, keyed by the
|
|
64
|
+
* durable turn number. The /usage panel prices its turns with this: the token
|
|
65
|
+
* meter says what each turn billed, this says what it cost.
|
|
66
|
+
* @param id - the session id.
|
|
67
|
+
* @param events - the session's durable events, for the turn windows.
|
|
68
|
+
* @returns `{ turn, cost, calls, unknown }` per completed or running turn.
|
|
69
|
+
*/
|
|
70
|
+
export function turnCostsFor(id, events = []) {
|
|
71
|
+
const entries = id ? readMetrics(resolveDshHome(), id).rows : [];
|
|
72
|
+
return attributeCostByTurn(entries, events).turns;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Terminal columns of a string: East Asian wide characters (such as the cache label's
|
|
76
|
+
* CJK glyphs) take two, and zero-width marks take none. The variation selectors matter
|
|
77
|
+
* here: `❄️` is U+2744 plus U+FE0F, and counting that selector as a column made the
|
|
78
|
+
* footer's provider budget swing with the DeepSeek pricing window.
|
|
79
|
+
*/
|
|
80
|
+
const WIDE = /[\u1100-\u115f\u2e80-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6\u2600-\u27bf\u{1f300}-\u{1faff}]/u;
|
|
81
|
+
// eslint-disable-next-line no-misleading-character-class -- these are zero width by design
|
|
82
|
+
const ZERO_WIDTH = /[\u0300-\u036f\u200d\ufe00-\ufe0f]/;
|
|
83
|
+
export function displayWidth(text) {
|
|
84
|
+
let width = 0;
|
|
85
|
+
for (const char of text) width += ZERO_WIDTH.test(char) ? 0 : WIDE.test(char) ? 2 : 1;
|
|
86
|
+
return width;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The money slot for the Grok subscription rail: a plan has credits and a reset time, not a
|
|
90
|
+
* bill. The percentage is optional (the server omits it for a period without usage), and an
|
|
91
|
+
* unread window shows the tier alone instead of a wrong number.
|
|
92
|
+
*/
|
|
93
|
+
export function grokFooterFact(subscription, locale = 'en', now = Date.now()) {
|
|
94
|
+
const parts = [subscription?.tier ?? 'Grok'];
|
|
95
|
+
const used = subscription?.usedPercent;
|
|
96
|
+
if (Number.isFinite(used)) parts.push((Number.isInteger(used) ? String(used) : used.toFixed(1)) + '% ' + t(locale, 'footer.grokUsed'));
|
|
97
|
+
const reset = resetStamp(subscription?.periodEnd, now);
|
|
98
|
+
if (reset !== undefined) parts.push(t(locale, 'footer.grokResets') + ' ' + reset);
|
|
99
|
+
return parts.join(' · ');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const GO_WINDOWS = Object.freeze([['rolling', '5h'], ['weekly', '7d'], ['monthly', '30d']]);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The money slot for the OpenCode Go subscription: the share used of each cap Go enforces —
|
|
106
|
+
* rolling five hours, week, month — and, when one is exhausted, when it lifts. An unread
|
|
107
|
+
* snapshot shows the plan name alone instead of a wrong number.
|
|
108
|
+
*/
|
|
109
|
+
export function goFooterFact(usage, locale = 'en', now = Date.now()) {
|
|
110
|
+
const parts = ['Go'];
|
|
111
|
+
for (const [key, label] of GO_WINDOWS) {
|
|
112
|
+
const percent = usage?.[key]?.percent;
|
|
113
|
+
if (Number.isFinite(percent)) parts.push(label + ' ' + (Number.isInteger(percent) ? String(percent) : percent.toFixed(1)) + '%' + (usage[key].limited ? '!' : ''));
|
|
114
|
+
}
|
|
115
|
+
// Blocked until the last exhausted window lifts: that is when requests work again.
|
|
116
|
+
const lifts = GO_WINDOWS.map(([key]) => usage?.[key]).filter(window => window?.limited).map(window => window.resetsAt)
|
|
117
|
+
.filter(iso => Number.isFinite(Date.parse(iso ?? ''))).sort((a, b) => Date.parse(b) - Date.parse(a))[0];
|
|
118
|
+
const stamp = resetStamp(lifts, now);
|
|
119
|
+
if (stamp !== undefined) parts.push(t(locale, 'footer.goLimited') + ' ' + stamp);
|
|
120
|
+
return parts.join(' · ');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Local `MM-DD HH:MM` for a reset time, or undefined when the window is unknown or past. */
|
|
124
|
+
function resetStamp(iso, now) {
|
|
125
|
+
const at = Date.parse(typeof iso === 'string' ? iso : '');
|
|
126
|
+
if (!Number.isFinite(at) || at <= now) return undefined;
|
|
127
|
+
const pad = value => String(value).padStart(2, '0');
|
|
128
|
+
const date = new Date(at);
|
|
129
|
+
return pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + ' ' + pad(date.getHours()) + ':' + pad(date.getMinutes());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Fixed figure columns for the live footer. The status line re-reads these
|
|
134
|
+
* figures every second, so a growing digit count must never move the segments
|
|
135
|
+
* after it: each figure is right-aligned inside its own columns, and only the
|
|
136
|
+
* terminal width decides which segments the drop ladder keeps.
|
|
137
|
+
*/
|
|
138
|
+
const FIGURE = {
|
|
139
|
+
/** Request and session-average rates share the same fixed figure width. */
|
|
140
|
+
rate: 6,
|
|
141
|
+
/** `100%` context occupancy. */
|
|
142
|
+
percent: 4,
|
|
143
|
+
/** `100.0%` cache share. */
|
|
144
|
+
share: 6,
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Columns {@link formatFooter} needs on top of the budget it is handed. The
|
|
149
|
+
* fixed figures above only stop moving once their columns are really reserved,
|
|
150
|
+
* so a caller that budgets the footer's width adds these columns first —
|
|
151
|
+
* otherwise the padding evicts a figure the width would have seated. Six
|
|
152
|
+
* measures the padding a live reading adds across the rate, context and cache
|
|
153
|
+
* figures at the widths the drop ladder actually decides on.
|
|
154
|
+
*/
|
|
155
|
+
export const FOOTER_FIGURE_RESERVE = 6;
|
|
156
|
+
|
|
157
|
+
/** Right-align one figure inside its columns; a wider reading keeps its own width. */
|
|
158
|
+
function figure(text, width) {
|
|
159
|
+
const padding = width - displayWidth(text);
|
|
160
|
+
return padding > 0 ? ' '.repeat(padding) + text : text;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function footerFigures(metrics, context, rates, locale = 'en', provider = 'deepseek-official') {
|
|
164
|
+
provider = canonicalProvider(provider);
|
|
165
|
+
const label = key => t(locale, key);
|
|
166
|
+
const ctx = figure(Number.isFinite(context) ? `${Math.round(context)}%` : '--', FIGURE.percent);
|
|
167
|
+
const cache = figure(metrics.cache === null ? '--' : `${metrics.cache.toFixed(1)}%`, FIGURE.share);
|
|
168
|
+
// The balance belongs to the provider serving the route; only DeepSeek's official one bills by a peak window.
|
|
169
|
+
const balance = balanceNow(provider);
|
|
170
|
+
const spend = metrics.unknown && metrics.cost === 0 ? '--' : `$${metrics.cost.toFixed(2)}${metrics.unknown ? '+' : ''}${metrics.pending ? '…' : ''}`;
|
|
171
|
+
// The money is the one live figure whose digits are not reserved: they cross a
|
|
172
|
+
// column a handful of times per session, and reserving them would evict a
|
|
173
|
+
// per-second figure at the widths the footer actually runs at. A balance the
|
|
174
|
+
// provider cannot report is left out instead of parked as `$--`.
|
|
175
|
+
const dollars = provider === 'grok' ? grokFooterFact(grokSubscriptionNow(), locale)
|
|
176
|
+
: provider === 'opencode-go' ? goFooterFact(goUsageNow(), locale)
|
|
177
|
+
: spend + (balance === null ? '' : ' / $' + balance.toFixed(2)) + (provider === 'deepseek-official' ? ' ' + peakEmoji(trustedNow()) : '');
|
|
178
|
+
// The budget slot rides the money figure: it answers "how much of the session's
|
|
179
|
+
// limit is spent", which is a fact about the cost, not a second cost. A session
|
|
180
|
+
// with no limit renders exactly as before, so the footer only grows by choice.
|
|
181
|
+
const budget = metrics.budget;
|
|
182
|
+
const budgeted = budget === undefined || budget.limit === null ? ''
|
|
183
|
+
: `/${budget.limit.toFixed(2)}${budget.state === 'over' ? '⚠!' : budget.state === 'warn' ? '⚠' : ''}`;
|
|
184
|
+
const money = dollars + budgeted;
|
|
185
|
+
// The last completed turn is the one figure the whole-session total cannot
|
|
186
|
+
// give: `$1.23 / $9.86 · #12 $0.04` reads "the session so far, of which the
|
|
187
|
+
// last turn cost this". `+` marks a turn whose settled calls were not all
|
|
188
|
+
// priceable, the same mark the total uses.
|
|
189
|
+
const turn = metrics.lastTurn === undefined || !Number.isFinite(metrics.lastTurn.cost) ? ''
|
|
190
|
+
: `#${metrics.lastTurn.turn} $${metrics.lastTurn.cost.toFixed(2)}${metrics.lastTurn.unknown ? '+' : ''}`;
|
|
191
|
+
return {
|
|
192
|
+
current: rates ? `${rates.active ? '◌' : ' '} ${figure(Number.isFinite(rates.current) ? rates.current.toFixed(1) : '--', FIGURE.rate)} tps` : '',
|
|
193
|
+
average: rates ? `${figure(Number.isFinite(rates.average) ? rates.average.toFixed(1) : '--', FIGURE.rate)} tps ${label('footer.average')}` : '',
|
|
194
|
+
context: `${ctx} ${label('footer.context')}`,
|
|
195
|
+
money,
|
|
196
|
+
cache: `${cache} ${label('footer.cache')}`,
|
|
197
|
+
turn,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Flat footer for text consumers; the TUI uses the same figures as separate groups. */
|
|
202
|
+
export function formatFooter(metrics, context, columns = 80, rates, locale = 'en', provider = 'deepseek-official') {
|
|
203
|
+
const figures = footerFigures(metrics, context, rates, locale, provider);
|
|
204
|
+
const { current, average, context: ctx, money, cache, turn } = figures;
|
|
205
|
+
const base = rates ? [current, average, ctx, money, cache] : [ctx, money, cache];
|
|
206
|
+
// The slot is reserved even when no turn has cost anything yet: the drop
|
|
207
|
+
// ladder's indices are fixed against this array, and an unfilled slot renders
|
|
208
|
+
// as '' (skipped by `render`) exactly like the absent figure would.
|
|
209
|
+
base.splice(rates ? 3 : 1, 0, turn);
|
|
210
|
+
// Narrow terminals shed the quietest figures first: the last turn, then
|
|
211
|
+
// average, current and context, and only then the cache. The running cost is
|
|
212
|
+
// the last thing standing, and its slot is index 4 (or 2 without rates) now
|
|
213
|
+
// that the turn slot holds index 3 (or 1) open.
|
|
214
|
+
const drops = rates ? [3, 1, 0, 2, 5, 4] : [1, 0, 3, 2];
|
|
215
|
+
const render = omit => base.filter((part, index) => part !== '' && !omit.has(index)).join(' · ');
|
|
216
|
+
for (let dropped = 0; dropped <= drops.length; dropped++) {
|
|
217
|
+
const value = render(new Set(drops.slice(0, dropped)));
|
|
218
|
+
if (displayWidth(value) <= columns) return value;
|
|
219
|
+
}
|
|
220
|
+
const floor = render(new Set(drops));
|
|
221
|
+
let clipped = '';
|
|
222
|
+
for (const char of floor) { if (displayWidth(clipped + char) > columns) break; clipped += char; }
|
|
223
|
+
return clipped;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* The session's recorded spend and whether any of it is unpriceable, for a host
|
|
227
|
+
* that must decide something (the budget gate) rather than only render it. Same
|
|
228
|
+
* ledger and same rules as {@link footerFor}, so the figure the gate compares
|
|
229
|
+
* and the figure the footer shows can never disagree.
|
|
230
|
+
*/
|
|
231
|
+
export function sessionSpend(id) {
|
|
232
|
+
try {
|
|
233
|
+
const ledger = id ? readMetrics(resolveDshHome(), id) : { rows: [], corrupt: false };
|
|
234
|
+
const data = id ? source?.(id) : undefined;
|
|
235
|
+
const summary = summarize(ledger.rows, data?.events ?? [], ledger.corrupt);
|
|
236
|
+
return { cost: summary.cost, unknown: summary.unknown, pending: summary.pending };
|
|
237
|
+
} catch { return { cost: 0, unknown: true, pending: 0 }; }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Per-events memo: the status line renders up to once a second, and summarize/average are O(events). */
|
|
241
|
+
const footerCache = new WeakMap();
|
|
242
|
+
export function footerFor(id, stats, columns, provider = 'deepseek-official', locale = 'en', format = formatFooter) {
|
|
243
|
+
const limit = parseBudget(process.env.DSCODE_SESSION_BUDGET_USD);
|
|
244
|
+
try {
|
|
245
|
+
// Live estimation can be unavailable while a Host/agent is reconnecting.
|
|
246
|
+
// Keep the durable prompt reading visible instead of losing all telemetry.
|
|
247
|
+
let data;
|
|
248
|
+
try { data = id ? source?.(id) : undefined; } catch { /* use the TUI's durable projection */ }
|
|
249
|
+
const ledger = id ? readMetrics(resolveDshHome(), id) : { rows: [], corrupt: false };
|
|
250
|
+
const events = data?.events ?? [];
|
|
251
|
+
// Identity alone is not enough: a session event list may be appended to in place.
|
|
252
|
+
const hit = events.length > 0 ? footerCache.get(events) : undefined;
|
|
253
|
+
const tail = events.at(-1)?.time;
|
|
254
|
+
const fresh = hit !== undefined && hit.key === ledger.rows && hit.length === events.length && hit.tail === tail;
|
|
255
|
+
const summary = fresh ? hit.summary : summarize(ledger.rows, events, ledger.corrupt);
|
|
256
|
+
const used = Number.isFinite(data?.used) && data.used >= 0 ? data.used
|
|
257
|
+
: Number.isFinite(stats.lastPromptTokens) && stats.lastPromptTokens > 0 ? stats.lastPromptTokens : undefined;
|
|
258
|
+
const capacity = Number.isFinite(data?.capacity) && data.capacity > 0 ? data.capacity : stats.contextWindow;
|
|
259
|
+
const average = fresh ? hit.average : sessionAverageTps(events);
|
|
260
|
+
if (events.length > 0 && !fresh) footerCache.set(events, { key: ledger.rows, length: events.length, tail, summary, average });
|
|
261
|
+
// The budget comes from the environment for this process only: it is a
|
|
262
|
+
// per-machine spending guard, not a session property worth persisting.
|
|
263
|
+
const budget = limit === null ? undefined : evaluateBudget(summary.cost, limit);
|
|
264
|
+
return format({ ...summary, ...(budget === undefined ? {} : { budget }) }, Number.isFinite(used) && capacity > 0 ? used / capacity * 100 : undefined, columns, { current: data?.currentTps, average, active: data?.requestActive }, locale, provider);
|
|
265
|
+
} catch { return format({ cost: 0, unknown: true, cache: null }, undefined, columns, { current: null, average: null }, locale, provider); }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** Structured figures preserve money/cache as one right-aligned group in the TUI. */
|
|
269
|
+
export function footerFiguresFor(id, stats, provider = 'deepseek-official', locale = 'en') {
|
|
270
|
+
return footerFor(id, stats, 0, provider, locale,
|
|
271
|
+
(metrics, context, _columns, rates, language, route) => footerFigures(metrics, context, rates, language, route));
|
|
272
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clock marks in the model's own context: every message a step admits, and
|
|
3
|
+
* every turn that closed before it, carries one reading of the host clock. A
|
|
4
|
+
* mark is its own plugin-sourced message rather than an edit of the prompt, so
|
|
5
|
+
* message bodies, session cards and title selection read exactly what they
|
|
6
|
+
* read before. Like the time-context reading it is durable: it spends model
|
|
7
|
+
* context from then on, replays on resume, and the terminal hides it by
|
|
8
|
+
* producer.
|
|
9
|
+
*
|
|
10
|
+
* @module dscode-time-marks
|
|
11
|
+
*/
|
|
12
|
+
import z from '@deepseek-ai/schemastery';
|
|
13
|
+
import { createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
14
|
+
import { arrivalMark, resolveTimeZone, turnEndMark } from './marks.mjs';
|
|
15
|
+
import { producerKind } from '../message-source/kind.mjs';
|
|
16
|
+
export const name = 'dscode-time-marks';
|
|
17
|
+
export const Config = z.object({
|
|
18
|
+
/** Fallback display zone; empty uses the host zone. */
|
|
19
|
+
timeZone: z.string().default(''),
|
|
20
|
+
});
|
|
21
|
+
/** Marks carried by one request at most, so a delivery burst cannot flood a step. */
|
|
22
|
+
const MARK_LIMIT = 8;
|
|
23
|
+
|
|
24
|
+
/** Recover bounded turn endings that no durable clock message has reported. */
|
|
25
|
+
export function pendingTimeMarks(events) {
|
|
26
|
+
const pending = [];
|
|
27
|
+
let covered = -1, crossedEnd = false;
|
|
28
|
+
for (let index = events.length - 1; index >= 0; index--) {
|
|
29
|
+
const event = events[index];
|
|
30
|
+
if (event.type === 'user/message' && producerKind(event.data.source) === name) {
|
|
31
|
+
// New clocks carry explicit IDs. Older clocks are trusted plugin output;
|
|
32
|
+
// their rendered turn lines are the available durable evidence.
|
|
33
|
+
const turns = event.data.source.closedTurns ?? (event.data.content ?? []).flatMap(block =>
|
|
34
|
+
block.type === 'text' ? [...block.text.matchAll(/^Time mark: .+ — turn (\d+) ended(?:,|\.)/gm)].map(match => +match[1]) : []);
|
|
35
|
+
if (Array.isArray(turns)) for (const turn of turns) if (Number.isSafeInteger(turn)) covered = Math.max(covered, turn);
|
|
36
|
+
}
|
|
37
|
+
if (event.type === 'turn/end') {
|
|
38
|
+
if (event.data.turn <= covered) crossedEnd = true;
|
|
39
|
+
else if (pending.length < MARK_LIMIT) pending.push({ turn: event.data.turn, endedAt: event.time });
|
|
40
|
+
}
|
|
41
|
+
if (event.type === 'turn/start') {
|
|
42
|
+
const ended = pending.find(item => item.turn === event.data.turn);
|
|
43
|
+
if (ended) ended.startedAt = event.time;
|
|
44
|
+
}
|
|
45
|
+
if ((crossedEnd || pending.length === MARK_LIMIT) && pending.every(item => item.startedAt !== undefined)) break;
|
|
46
|
+
}
|
|
47
|
+
return pending.reverse();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Per-agent mark state: closed turns still waiting for a step to carry them,
|
|
51
|
+
* and the opening time of the turn each one closed.
|
|
52
|
+
*/
|
|
53
|
+
export function apply(ctx, config = {}) {
|
|
54
|
+
return install(ctx, config, () => true);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function applyDesktop(ctx, config = {}) {
|
|
58
|
+
return install(ctx, config, agent => ctx.agentPresets.composedPreset(agent.ctx) === 'dscode');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function install(ctx, config, accepts) {
|
|
62
|
+
const marks = new WeakMap();
|
|
63
|
+
const stateFor = agent => {
|
|
64
|
+
let state = marks.get(agent);
|
|
65
|
+
if (state === undefined) marks.set(agent, state = { turns: pendingTimeMarks(agent.session?.snapshotEvents?.() ?? []), starts: new Map() });
|
|
66
|
+
return state;
|
|
67
|
+
};
|
|
68
|
+
const timeZone = resolveTimeZone(config.timeZone);
|
|
69
|
+
let closed = false;
|
|
70
|
+
ctx.effect(() => () => { closed = true; });
|
|
71
|
+
ctx.on('agent/pre-step', async (payload, next) => {
|
|
72
|
+
if (closed || !accepts(payload.agent)) return next();
|
|
73
|
+
const state = stateFor(payload.agent);
|
|
74
|
+
// Keep inbox provenance before downstream handlers add runtime context or
|
|
75
|
+
// filter the batch. Some native additions (e.g. skill-catalog) have no form.
|
|
76
|
+
const arrivals = new Set(payload.messages.map(message => message.id));
|
|
77
|
+
// The first step of a turn is the only moment its opening is observable.
|
|
78
|
+
if (payload.step === 1) state.starts.set(payload.turn, Date.now());
|
|
79
|
+
const decision = await next();
|
|
80
|
+
if (closed || decision.kind !== 'enter') return decision;
|
|
81
|
+
const at = Date.now();
|
|
82
|
+
const lines = [], closedTurns = [];
|
|
83
|
+
for (const message of decision.messages) {
|
|
84
|
+
if (!arrivals.has(message.id)) continue;
|
|
85
|
+
// Marks are injected context, never an inbox arrival; skipping our own
|
|
86
|
+
// producer keeps a future delivery path from marking itself.
|
|
87
|
+
if (producerKind(message.source) === name) continue;
|
|
88
|
+
const line = arrivalMark(message, at, timeZone);
|
|
89
|
+
if (line !== null && lines.length < MARK_LIMIT) lines.push(line);
|
|
90
|
+
}
|
|
91
|
+
// An arrival burst that fills the limit leaves the remaining turn marks
|
|
92
|
+
// queued: the next step carries them instead of losing them here.
|
|
93
|
+
while (lines.length < MARK_LIMIT && state.turns.length > 0) {
|
|
94
|
+
const ended = state.turns.shift();
|
|
95
|
+
lines.push(turnEndMark(ended.turn, ended.endedAt, ended.startedAt, timeZone));
|
|
96
|
+
closedTurns.push(ended.turn);
|
|
97
|
+
}
|
|
98
|
+
if (lines.length === 0) return decision;
|
|
99
|
+
return {
|
|
100
|
+
...decision,
|
|
101
|
+
// Ahead of the admitted batch: clocks must not take the prompt's place.
|
|
102
|
+
// The Host may independently append its runtime context after the input.
|
|
103
|
+
messages: [createUserMessage({
|
|
104
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
105
|
+
source: { kind: name, form: 'snapshot', closedTurns },
|
|
106
|
+
}), ...decision.messages],
|
|
107
|
+
};
|
|
108
|
+
}, { prepend: true });
|
|
109
|
+
ctx.on('agent/turn-stopping', payload => {
|
|
110
|
+
if (closed || !accepts(payload.agent)) return;
|
|
111
|
+
const state = stateFor(payload.agent);
|
|
112
|
+
state.turns.push({ turn: payload.turn, endedAt: Date.now(), startedAt: state.starts.get(payload.turn) });
|
|
113
|
+
state.starts.delete(payload.turn);
|
|
114
|
+
if (state.turns.length > MARK_LIMIT) state.turns.length = MARK_LIMIT;
|
|
115
|
+
});
|
|
116
|
+
}
|