carthagent 0.0.0-stage → 1.0.7
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 +219 -2
- package/bin/ctg.mjs +736 -0
- package/docs/budgets.md +72 -0
- package/docs/compaction.md +34 -0
- package/docs/context-efficiency-plan.md +24 -0
- package/docs/context-efficiency-review.md +27 -0
- package/docs/context-efficiency.md +49 -0
- package/docs/deepseek-jev-benchmark-plan.md +70 -0
- package/docs/deepseek-jev-benchmark-repair-plan.md +20 -0
- package/docs/deepseek-jev-benchmark-restart.md +9 -0
- package/docs/deepseek-jev-benchmark-results.md +77 -0
- package/docs/deepseek-jev-benchmark-running.md +60 -0
- package/docs/deepseek-jev-benchmark-v3-results.md +41 -0
- package/docs/deepseek-jev-benchmark-v4-protocol.md +15 -0
- package/docs/deepseek-jev-benchmark-v4-results.md +119 -0
- package/docs/deepseek-jev-http400-diagnosis.md +45 -0
- package/docs/delivery-review-evidence.md +53 -0
- package/docs/delivery-timeout-repair-plan.md +51 -0
- package/docs/evaluation-readiness.md +31 -0
- package/docs/evaluation.md +70 -0
- package/docs/extending.md +354 -0
- package/docs/glm-pilot-execution.md +9 -0
- package/docs/glm-pilot-plan.md +15 -0
- package/docs/glm-pilot-results.md +71 -0
- package/docs/graph-context-optimization.md +15 -0
- package/docs/jev-repository-pilot-plan.md +25 -0
- package/docs/jev-repository-pilot-protocol.md +39 -0
- package/docs/jev-repository-pilot-restart.md +7 -0
- package/docs/jev-repository-pilot-results.md +79 -0
- package/docs/jev.md +32 -0
- package/docs/model-registry.md +25 -0
- package/docs/native-compatibility.md +117 -0
- package/docs/opencode-archive.md +9 -0
- package/docs/opencode-migration.md +115 -0
- package/docs/opencode-runtime.md +94 -0
- package/docs/opencode-workspace.md +31 -0
- package/docs/owned-runtime-workflow-registration.md +17 -0
- package/docs/plan-advisor-evaluation.md +38 -0
- package/docs/planning-startup-fix-plan.md +23 -0
- package/docs/planning-startup-fix-review.md +24 -0
- package/docs/planning.md +118 -0
- package/docs/production-merge-review.md +23 -0
- package/docs/quality.md +35 -0
- package/docs/repository-benchmark-design.md +111 -0
- package/docs/review-assurance.md +9 -0
- package/docs/review-experiment-results.md +59 -0
- package/docs/tested-planning-roadmap.md +58 -0
- package/docs/tested-planning.md +58 -0
- package/docs/tui-pickers.md +9 -0
- package/docs/webdriver.md +83 -0
- package/docs/workflow-modes.md +26 -0
- package/docs/workflow.d2 +17 -0
- package/docs/workflow.svg +149 -0
- package/docs/workspace-coordination.md +9 -0
- package/evaluate.mjs +249 -0
- package/extensions/codegraph.ts +36 -0
- package/extensions/delivery.ts +819 -0
- package/extensions/experiential.ts +21 -0
- package/extensions/subscriptions.ts +21 -0
- package/guidance/profiles/authenticated-web.json +24 -0
- package/guidance/profiles/data-visualization.json +25 -0
- package/guidance/profiles/database-change.json +24 -0
- package/guidance/profiles/external-api.json +24 -0
- package/guidance/profiles/transactional-data.json +25 -0
- package/guidance/profiles/web-application.json +26 -0
- package/lib/acceptance-review.mjs +4 -0
- package/lib/browser-bin.mjs +160 -0
- package/lib/browser-errors.mjs +54 -0
- package/lib/browser.mjs +219 -0
- package/lib/budget.mjs +37 -0
- package/lib/cdp.mjs +221 -0
- package/lib/cloud/client.mjs +275 -0
- package/lib/cloud/control-plane.mjs +350 -0
- package/lib/cloud/gateway.mjs +380 -0
- package/lib/cloud/http.mjs +135 -0
- package/lib/cloud/ledger.mjs +215 -0
- package/lib/codegraph-query.mjs +60 -0
- package/lib/codegraph.mjs +378 -0
- package/lib/context-archive.mjs +18 -0
- package/lib/delivery-context.mjs +132 -0
- package/lib/delivery-guidance.mjs +9 -0
- package/lib/delivery-review.mjs +94 -0
- package/lib/delivery.mjs +610 -0
- package/lib/engine.mjs +137 -0
- package/lib/guidance.mjs +71 -0
- package/lib/jev-advisor.mjs +47 -0
- package/lib/opencode/archive.mjs +122 -0
- package/lib/opencode/compatibility.mjs +19 -0
- package/lib/opencode/credentials.mjs +205 -0
- package/lib/opencode/evaluation-budget-plugin/index.ts +147 -0
- package/lib/opencode/evaluation.mjs +242 -0
- package/lib/opencode/file-lock.mjs +86 -0
- package/lib/opencode/headless.mjs +200 -0
- package/lib/opencode/review.mjs +213 -0
- package/lib/opencode/runtime.mjs +246 -0
- package/lib/opencode/service.mjs +249 -0
- package/lib/opencode/session-options.mjs +60 -0
- package/lib/opencode/workspace.mjs +133 -0
- package/lib/openrouter-jev.mjs +37 -0
- package/lib/outcome-contract.mjs +21 -0
- package/lib/plan-advisor.mjs +38 -0
- package/lib/planning-access.mjs +7 -0
- package/lib/planning-advice.mjs +28 -0
- package/lib/planning-discovery.mjs +14 -0
- package/lib/planning-guide.mjs +34 -0
- package/lib/planning-view.mjs +57 -0
- package/lib/planning.mjs +155 -0
- package/lib/probe-diagnostics.mjs +13 -0
- package/lib/providers/experiential.mjs +292 -0
- package/lib/providers/model-catalog.json +700 -0
- package/lib/providers/model-catalog.mjs +7 -0
- package/lib/providers/names.mjs +13 -0
- package/lib/providers/subscription-models.mjs +483 -0
- package/lib/reports.mjs +80 -0
- package/lib/review-state.mjs +49 -0
- package/lib/review.mjs +114 -0
- package/lib/scenarios.mjs +99 -0
- package/lib/ship-auth.mjs +44 -0
- package/lib/ship-jev.mjs +59 -0
- package/lib/ship-telemetry.mjs +183 -0
- package/lib/tui/ansi.mjs +255 -0
- package/lib/tui/app.mjs +2445 -0
- package/lib/tui/auth.mjs +249 -0
- package/lib/tui/carthage.mjs +109 -0
- package/lib/tui/clipboard.mjs +122 -0
- package/lib/tui/d2.mjs +431 -0
- package/lib/tui/feed.mjs +467 -0
- package/lib/tui/framing.mjs +80 -0
- package/lib/tui/glyphs.mjs +172 -0
- package/lib/tui/hash-worker.mjs +11 -0
- package/lib/tui/mock-engine.mjs +222 -0
- package/lib/tui/models.mjs +73 -0
- package/lib/tui/picker.mjs +18 -0
- package/lib/tui/theme.mjs +663 -0
- package/lib/tui/title.mjs +7 -0
- package/lib/update-channel.mjs +62 -0
- package/lib/update.mjs +230 -0
- package/lib/webdriver.mjs +82 -0
- package/lib/workflow.mjs +88 -0
- package/lib/workspace.mjs +39 -0
- package/metadata.json +8 -0
- package/package.json +94 -4
- package/plugins/carthagent/index.ts +3 -0
- package/plugins/carthagent/package.json +28 -0
- package/plugins/carthagent/src/archive-rpc.ts +46 -0
- package/plugins/carthagent/src/bootstrap.ts +38 -0
- package/plugins/carthagent/src/index.ts +62 -0
- package/plugins/carthagent/src/pull-request-rpc.ts +54 -0
- package/plugins/carthagent/src/rpc.ts +76 -0
- package/plugins/carthagent/src/server/archive-rpc.ts +327 -0
- package/plugins/carthagent/src/server/budgets.ts +21 -0
- package/plugins/carthagent/src/server/codegraph.ts +46 -0
- package/plugins/carthagent/src/server/delivery.ts +488 -0
- package/plugins/carthagent/src/server/device-auth.ts +91 -0
- package/plugins/carthagent/src/server/native-delivery.ts +26 -0
- package/plugins/carthagent/src/server/orchestration.ts +56 -0
- package/plugins/carthagent/src/server/policy.ts +24 -0
- package/plugins/carthagent/src/server/providers.ts +424 -0
- package/plugins/carthagent/src/server/pull-request-rpc.ts +387 -0
- package/plugins/carthagent/src/server/session-controls.ts +82 -0
- package/plugins/carthagent/src/server/telemetry.ts +92 -0
- package/plugins/carthagent/src/server/workspace-rpc.ts +349 -0
- package/plugins/carthagent/src/session-rpc.ts +31 -0
- package/plugins/carthagent/src/testing.ts +85 -0
- package/plugins/carthagent/src/workspace-rpc.ts +133 -0
- package/plugins/carthagent/test-plugin/index.ts +4 -0
- package/plugins/carthagent/test-plugin/package.json +1 -0
- package/plugins/carthagent/tsconfig.json +19 -0
- package/plugins/carthagent/tui.ts +1 -0
- package/prompts/delivery.md +7 -0
- package/public/index.html +983 -0
- package/scenarios/cli/grade_cli.py +69 -0
- package/scenarios/cli/scenario.json +21 -0
- package/scenarios/game/baseline/minecraft.py +193 -0
- package/scenarios/game/context.md +1 -0
- package/scenarios/game/grade_minecraft_clone.py +603 -0
- package/scenarios/game/requirements.txt +2 -0
- package/scenarios/game/scenario.json +33 -0
- package/server.mjs +341 -0
- package/skills/game-development/SKILL.md +36 -0
- package/skills/game-development/assets/ursina_starter.py +98 -0
- package/skills/game-development/scripts/verify_ursina.py +80 -0
- package/skills/software-delivery/SKILL.md +50 -0
- package/skills/typescript-delivery/SKILL.md +69 -0
- package/tests/acceptance-review.test.mjs +13 -0
- package/tests/auth.test.mjs +234 -0
- package/tests/bounded-summary.test.mjs +68 -0
- package/tests/browser-bin.test.mjs +91 -0
- package/tests/browser.test.mjs +96 -0
- package/tests/budget-engine.test.mjs +110 -0
- package/tests/budget.test.mjs +23 -0
- package/tests/cli.test.mjs +245 -0
- package/tests/clipboard.test.mjs +79 -0
- package/tests/cloud-client.test.mjs +129 -0
- package/tests/cloud-control-plane.test.mjs +144 -0
- package/tests/cloud-gateway.test.mjs +147 -0
- package/tests/cloud-http.test.mjs +102 -0
- package/tests/cloud-ledger.test.mjs +75 -0
- package/tests/codegraph-query.test.mjs +66 -0
- package/tests/codegraph.test.mjs +142 -0
- package/tests/delivery-context.test.mjs +66 -0
- package/tests/delivery-review.test.mjs +139 -0
- package/tests/delivery.test.mjs +343 -0
- package/tests/engine-api.mjs +24 -0
- package/tests/experiential-extension.test.mjs +37 -0
- package/tests/experiential.test.mjs +300 -0
- package/tests/extension.test.mjs +921 -0
- package/tests/fixtures/archive-plugin/index.ts +10 -0
- package/tests/guidance.test.mjs +78 -0
- package/tests/model-registry.test.mjs +68 -0
- package/tests/models.test.mjs +70 -0
- package/tests/openai-idempotency.test.mjs +99 -0
- package/tests/opencode-archive-rpc.test.mjs +82 -0
- package/tests/opencode-delivery.test.mjs +231 -0
- package/tests/opencode-providers.test.mjs +414 -0
- package/tests/opencode-runtime.test.mjs +455 -0
- package/tests/opencode-session-controls.test.mjs +115 -0
- package/tests/opencode-session-options.test.mjs +26 -0
- package/tests/opencode-workspace.test.mjs +339 -0
- package/tests/outcome-contract.test.mjs +17 -0
- package/tests/picker.test.mjs +34 -0
- package/tests/plan-advisor.test.mjs +88 -0
- package/tests/plan-review-loop.test.mjs +53 -0
- package/tests/planning-advice.test.mjs +27 -0
- package/tests/planning-contract.test.mjs +65 -0
- package/tests/planning-startup.test.mjs +38 -0
- package/tests/planning-view.test.mjs +33 -0
- package/tests/planning.test.mjs +223 -0
- package/tests/probe-diagnostics.test.mjs +21 -0
- package/tests/pull-request-rpc.test.mjs +366 -0
- package/tests/review-state.test.mjs +32 -0
- package/tests/review.test.mjs +127 -0
- package/tests/server.test.mjs +252 -0
- package/tests/ship-jev.test.mjs +77 -0
- package/tests/ship-telemetry.test.mjs +333 -0
- package/tests/standard-workflow.test.mjs +28 -0
- package/tests/subscription-models.test.mjs +416 -0
- package/tests/subscriptions-extension.test.mjs +27 -0
- package/tests/terminal-title.test.mjs +13 -0
- package/tests/test_grade_minecraft_clone.py +199 -0
- package/tests/test_starter.py +78 -0
- package/tests/theme.test.mjs +175 -0
- package/tests/tui-copy-paste.test.mjs +54 -0
- package/tests/tui.test.mjs +808 -0
- package/tests/update-channels.test.mjs +129 -0
- package/tests/update.test.mjs +175 -0
- package/tests/webdriver.test.mjs +31 -0
- package/tests/workflow.test.mjs +47 -0
- package/tests/workspace-rpc.test.mjs +236 -0
- package/tests/workspace.test.mjs +24 -0
- package/tools/benchmark/advisor.test.mjs +7 -0
- package/tools/benchmark/advisor.ts +27 -0
- package/tools/benchmark/campaign.mjs +45 -0
- package/tools/benchmark/diagnose-request.mjs +40 -0
- package/tools/benchmark/eval-common.mjs +32 -0
- package/tools/benchmark/eval-ledger.mjs +22 -0
- package/tools/benchmark/eval-shop.mjs +26 -0
- package/tools/benchmark/eval-voxel.mjs +20 -0
- package/tools/benchmark/evaluate.mjs +24 -0
- package/tools/benchmark/evaluators.test.mjs +13 -0
- package/tools/benchmark/fixtures/shop/server.mjs +18 -0
- package/tools/benchmark/fixtures/shop/ui.html +8 -0
- package/tools/benchmark/freeze.mjs +30 -0
- package/tools/benchmark/gateway.mjs +91 -0
- package/tools/benchmark/gateway.test.mjs +38 -0
- package/tools/benchmark/generator.mjs +11 -0
- package/tools/benchmark/generator.test.mjs +26 -0
- package/tools/benchmark/guard.test.mjs +133 -0
- package/tools/benchmark/guard.ts +136 -0
- package/tools/benchmark/native-plugin/index.ts +24 -0
- package/tools/benchmark/planning-startup.test.mjs +47 -0
- package/tools/benchmark/preflight-live.mjs +31 -0
- package/tools/benchmark/preflight.mjs +22 -0
- package/tools/benchmark/replay-diagnostic.mjs +23 -0
- package/tools/benchmark/runner.mjs +141 -0
- package/tools/benchmark/runner.test.mjs +93 -0
- package/tools/benchmark/runtime.mjs +23 -0
- package/tools/benchmark/selftest.mjs +47 -0
- package/tools/benchmark/smoke-jev.mjs +5 -0
- package/tools/benchmark/tasks.mjs +20 -0
- package/tools/benchmark/transport.mjs +65 -0
- package/tools/benchmark/transport.test.mjs +44 -0
- package/tools/benchmark/upstream-error.mjs +18 -0
- package/tools/benchmark/upstream-error.test.mjs +10 -0
- package/tools/browser-check.mjs +109 -0
- package/tools/check-syntax.mjs +27 -0
- package/tools/evaluate-plan-advisor.mjs +20 -0
- package/tools/native-fixture.mjs +194 -0
- package/tools/native-pty-smoke.py +108 -0
- package/tools/native-report-plugin/index.ts +50 -0
- package/tools/native-report-plugin/package.json +7 -0
- package/tools/patch-compaction.mjs +24 -0
- package/tools/patch-model-catalog.mjs +12 -0
- package/tools/patch-print-shutdown.mjs +39 -0
- package/tools/repo-pilot/evaluator.cjs +32 -0
- package/tools/repo-pilot/reference.mjs +43 -0
- package/tools/repo-pilot/runner.mjs +85 -0
- package/tools/repo-pilot/sandbox-entry.mjs +12 -0
- package/tools/repo-pilot/tasks.mjs +20 -0
- package/tools/repo-pilot/type-evaluator.cjs +6 -0
- package/tools/test-native-controls.mjs +84 -0
- package/tools/test-native-delivery-host.mjs +307 -0
- package/tools/test-native-evaluation.mjs +46 -0
- package/tools/test-native-host.mjs +385 -0
- package/tools/test-native-outcomes.mjs +73 -0
- package/tools/test-native-package.mjs +70 -0
- package/tools/test-native-pty.mjs +20 -0
- package/tools/test-native-report.mjs +88 -0
- package/tools/test-native-swe-host.mjs +132 -0
- package/tools/test-native-swe.mjs +122 -0
- package/tools/test-picker-pty.py +45 -0
- package/tools/test-python.mjs +29 -0
- package/tools/test-webdriver.mjs +54 -0
- package/tools/verify-release-version.mjs +37 -0
- package/tools/webdriver-check.mjs +52 -0
- package/vendor/agent/bounded-summary.mjs +88 -0
- package/vendor/agent/chunks/anthropic-messages-VWZZOSJQ.js +6 -0
- package/vendor/agent/chunks/anthropic.js +75 -0
- package/vendor/agent/chunks/azure-openai-responses-BJLAM6CD.js +2 -0
- package/vendor/agent/chunks/bedrock-converse-stream.js +74 -0
- package/vendor/agent/chunks/chord-context.js +96 -0
- package/vendor/agent/chunks/chunk-4L3WN2XY.js +2 -0
- package/vendor/agent/chunks/chunk-6CYCI3EJ.js +12 -0
- package/vendor/agent/chunks/chunk-6R7UI4IY.js +2 -0
- package/vendor/agent/chunks/chunk-7ZC4UXSL.js +2 -0
- package/vendor/agent/chunks/chunk-AXIIZGTV.js +2 -0
- package/vendor/agent/chunks/chunk-CLNPYIDP.js +42 -0
- package/vendor/agent/chunks/chunk-DRQDKNXA.js +2 -0
- package/vendor/agent/chunks/chunk-GMWCTUPB.js +2 -0
- package/vendor/agent/chunks/chunk-IDDQWTHI.js +2 -0
- package/vendor/agent/chunks/chunk-JVUZSMYM.js +1548 -0
- package/vendor/agent/chunks/chunk-L2ZPNLYD.js +2 -0
- package/vendor/agent/chunks/chunk-NUHFSC37.js +25 -0
- package/vendor/agent/chunks/chunk-OUXLLA64.js +11 -0
- package/vendor/agent/chunks/chunk-PGXDEOVV.js +2 -0
- package/vendor/agent/chunks/chunk-PJACZCIR.js +2 -0
- package/vendor/agent/chunks/chunk-PRRNXB7S.js +2 -0
- package/vendor/agent/chunks/chunk-TEPMHNKQ.js +2 -0
- package/vendor/agent/chunks/chunk-TRDNDS6A.js +4 -0
- package/vendor/agent/chunks/chunk-U6ZMQKGD.js +50 -0
- package/vendor/agent/chunks/chunk-UAQELI3K.js +2 -0
- package/vendor/agent/chunks/chunk-USJZMAIR.js +2 -0
- package/vendor/agent/chunks/chunk-XDUWOHPL.js +6 -0
- package/vendor/agent/chunks/chunk-XNGRGP62.js +2 -0
- package/vendor/agent/chunks/github-copilot.js +2 -0
- package/vendor/agent/chunks/google-generative-ai-Q2K7FBDI.js +2 -0
- package/vendor/agent/chunks/google-vertex-7UJBF4S7.js +2 -0
- package/vendor/agent/chunks/https-proxy-agent-2VXB7436.js +13 -0
- package/vendor/agent/chunks/image-resize-worker.js +2 -0
- package/vendor/agent/chunks/kimi-coding.js +2 -0
- package/vendor/agent/chunks/lib-HGQMNYVT.js +9 -0
- package/vendor/agent/chunks/mistral-conversations-4M6BZBX6.js +5 -0
- package/vendor/agent/chunks/multipart-parser-SQ6GDJL7.js +3 -0
- package/vendor/agent/chunks/node-KG362ECQ.js +16 -0
- package/vendor/agent/chunks/node-TWVVAGPO.js +2 -0
- package/vendor/agent/chunks/openai-codex-responses-R6VYZTVY.js +10 -0
- package/vendor/agent/chunks/openai-codex.js +75 -0
- package/vendor/agent/chunks/openai-completions-EKZT2IH2.js +7 -0
- package/vendor/agent/chunks/openai-responses-TFDINO6W.js +2 -0
- package/vendor/agent/chunks/openrouter-images-N4XVVFSD.js +2 -0
- package/vendor/agent/chunks/openrouter.js +75 -0
- package/vendor/agent/chunks/pi-messages-PML4SPVJ.js +8 -0
- package/vendor/agent/chunks/radius.js +75 -0
- package/vendor/agent/chunks/src-UK77SYZE.js +4 -0
- package/vendor/agent/chunks/xai.js +2 -0
- package/vendor/agent/cli.js +3 -0
- package/vendor/agent/dist/modes/interactive/theme/dark.json +91 -0
- package/vendor/agent/dist/modes/interactive/theme/light.json +90 -0
- package/vendor/agent/dist/modes/interactive/theme/theme-schema.json +356 -0
- package/vendor/agent/index.js +2 -0
- package/vendor/agent/modes/interactive/theme/dark.json +91 -0
- package/vendor/agent/modes/interactive/theme/light.json +90 -0
- package/vendor/agent/modes/interactive/theme/theme-schema.json +356 -0
- package/vendor/agent/package.json +14 -0
- package/vendor/agent/rpc-entry.js +3 -0
- package/vendor/agent/theme/dark.json +91 -0
- package/vendor/agent/theme/light.json +90 -0
- package/vendor/agent/theme/theme-schema.json +356 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal glyph registry.
|
|
3
|
+
*
|
|
4
|
+
* carthagent draws a lot of icons — tool marks, status dots, a D2 graph and progress
|
|
5
|
+
* spinners. Fonts that lack Geometric Shapes / Braille / Misc Symbols render
|
|
6
|
+
* those codepoints as `?`, which makes the console unreadable. This module
|
|
7
|
+
* centralizes every non-text glyph behind two complete tiers:
|
|
8
|
+
*
|
|
9
|
+
* unicode — the designed set (box drawing + widely supported shapes).
|
|
10
|
+
* ascii — a pure-ASCII fallback for limited fonts and legacy consoles.
|
|
11
|
+
*
|
|
12
|
+
* The tier is resolved once from `--ascii` / `CARTHAGENT_GLYPHS` / `CARTHAGENT_ASCII` and,
|
|
13
|
+
* in `auto` mode, from the locale + terminal markers. Consumers receive a flat
|
|
14
|
+
* object whose values are all single display cells (spinner is an array).
|
|
15
|
+
*
|
|
16
|
+
* When adding a glyph: give it an entry in BOTH tiers. Box drawing stays
|
|
17
|
+
* Unicode in the `unicode` tier because every monospace font ships it; the
|
|
18
|
+
* `ascii` tier replaces it with `+ - | ' .` so the graph survives anywhere.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const U = {
|
|
22
|
+
// --- tool / activity icons ---------------------------------------------
|
|
23
|
+
read: '\u25c8', // ◈
|
|
24
|
+
run: '\u25b8', // ▸
|
|
25
|
+
write: '\u270e', // ✎
|
|
26
|
+
search: '\u2315', // ⌕
|
|
27
|
+
list: '\u25a4', // ▤
|
|
28
|
+
plan: '\u25c7', // ◇
|
|
29
|
+
check: '\u2691', // ⚑
|
|
30
|
+
status: '\u00b7', // ·
|
|
31
|
+
|
|
32
|
+
// --- status marks -------------------------------------------------------
|
|
33
|
+
done: '\u2713', // ✓
|
|
34
|
+
fail: '\u2717', // ✗
|
|
35
|
+
warn: '\u25b2', // ▲
|
|
36
|
+
stale: '\u25d0', // ◐
|
|
37
|
+
blocked: '\u25a0', // ■
|
|
38
|
+
active: '\u25b6', // ▶
|
|
39
|
+
pending: '\u25cb', // ○
|
|
40
|
+
running: '\u25cf', // ●
|
|
41
|
+
queued: '\u25cc', // ◌
|
|
42
|
+
info: '\u00b7', // ·
|
|
43
|
+
aleph: '\u{10900}', // 𐤀 — Phoenician aleph, Carthagent's Aleph mark
|
|
44
|
+
sparkle: '\u2726', // ✦
|
|
45
|
+
sparkleHollow: '\u2727', // ✧
|
|
46
|
+
|
|
47
|
+
// --- navigation / decoration -------------------------------------------
|
|
48
|
+
up: '\u2191', // ↑
|
|
49
|
+
down: '\u2193', // ↓
|
|
50
|
+
left: '\u2190', // ←
|
|
51
|
+
right: '\u2192', // →
|
|
52
|
+
shift: '\u21e7', // ⇧
|
|
53
|
+
bullet: '\u2022', // •
|
|
54
|
+
ellipsis: '\u2026', // …
|
|
55
|
+
fold: '\u22ee', // ⋮
|
|
56
|
+
back: '\u21a9', // ↩
|
|
57
|
+
tail: '\u23bf', // ⎿ (tool-output gutter)
|
|
58
|
+
// pane scrollbars (box drawing — present in every monospace font)
|
|
59
|
+
scrollTrack: '\u2502', // │
|
|
60
|
+
scrollThumb: '\u2503', // ┃
|
|
61
|
+
// phase strip dots
|
|
62
|
+
phaseDone: '\u25cf', // ●
|
|
63
|
+
phaseCurrent: '\u25c9',// ◉
|
|
64
|
+
phasePending: '\u25cb',// ○
|
|
65
|
+
|
|
66
|
+
// --- D2 graph animation -------------------------------------------------
|
|
67
|
+
arrowDown: '\u25bc', // ▼
|
|
68
|
+
arrowDownHollow: '\u25bd', // ▽
|
|
69
|
+
arrowLeft: '\u25c0', // ◀
|
|
70
|
+
arrowLeftHollow: '\u25c1', // ◁
|
|
71
|
+
packetOn: '\u25cf', // ●
|
|
72
|
+
packetOff: '\u25e6', // ◦
|
|
73
|
+
|
|
74
|
+
// --- D2 graph box drawing ----------------------------------------------
|
|
75
|
+
box: {
|
|
76
|
+
tl: '\u256d', tr: '\u256e', bl: '\u2570', br: '\u256f', h: '\u2500', v: '\u2502',
|
|
77
|
+
mtl: '\u2554', mtr: '\u2557', mbl: '\u255a', mbr: '\u255d', mh: '\u2550', mv: '\u2551',
|
|
78
|
+
topJoin: '\u252c', botJoin: '\u2534', topJoinM: '\u2564', botJoinM: '\u2567', tee: '\u251c',
|
|
79
|
+
elbowDR: '\u256d', elbowDL: '\u256e', elbowUR: '\u2570', elbowUL: '\u256f',
|
|
80
|
+
dashV: '\u2504', dashH: '\u254c',
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// --- spinners -----------------------------------------------------------
|
|
84
|
+
spinner: ['\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f'],
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const A = {
|
|
88
|
+
read: 'r', run: '>', write: 'w', search: '/', list: '=',
|
|
89
|
+
plan: '+', check: '!', status: '-',
|
|
90
|
+
|
|
91
|
+
done: '+', fail: 'x', warn: '!', stale: '~', blocked: '#',
|
|
92
|
+
aleph: 'A',
|
|
93
|
+
sparkle: '*', sparkleHollow: '*',
|
|
94
|
+
active: '>', pending: '.', running: '*', queued: ':', info: '-',
|
|
95
|
+
|
|
96
|
+
up: '^', down: 'v', left: '<', right: '>', shift: '^',
|
|
97
|
+
bullet: '*', ellipsis: '..', fold: ':', back: '<-', tail: '\\',
|
|
98
|
+
scrollTrack: '|', scrollThumb: '#',
|
|
99
|
+
phaseDone: '*', phaseCurrent: '@', phasePending: '.',
|
|
100
|
+
|
|
101
|
+
arrowDown: 'v', arrowDownHollow: 'v', arrowLeft: '<', arrowLeftHollow: '<',
|
|
102
|
+
packetOn: '*', packetOff: 'o',
|
|
103
|
+
|
|
104
|
+
box: {
|
|
105
|
+
tl: '.', tr: '.', bl: "'", br: "'", h: '-', v: '|',
|
|
106
|
+
mtl: '+', mtr: '+', mbl: '+', mbr: '+', mh: '=', mv: '|',
|
|
107
|
+
topJoin: '+', botJoin: '+', topJoinM: '+', botJoinM: '+', tee: '+',
|
|
108
|
+
elbowDR: '.', elbowDL: '.', elbowUR: "'", elbowUL: "'",
|
|
109
|
+
dashV: ':', dashH: '.',
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
spinner: ['|', '/', '-', '\\'],
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const UNICODE_GLYPHS = Object.freeze({ ...U, box: Object.freeze({ ...U.box }), spinner: Object.freeze([...U.spinner]) });
|
|
116
|
+
export const ASCII_GLYPHS = Object.freeze({ ...A, box: Object.freeze({ ...A.box }), spinner: Object.freeze([...A.spinner]) });
|
|
117
|
+
|
|
118
|
+
const KNOWN_UTF8_TERMINALS = ['WT_SESSION', 'TERM_PROGRAM', 'ITERM_SESSION_ID', 'KITTY_WINDOW_ID', 'WEZTERM_EXECUTABLE', 'ALACRITTY_WINDOW_ID', 'GHOSTTY_RESOURCES_DIR', 'VTE_VERSION', 'KONSOLE_VERSION', 'TMUX', 'STY'];
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Pick a glyph tier. Explicit config always wins; otherwise `auto` keeps the
|
|
122
|
+
* Unicode set on terminals we can reasonably expect to support it and falls
|
|
123
|
+
* back to ASCII everywhere else (non-UTF-8 locale, legacy Windows console,
|
|
124
|
+
* unknown TERM). `NO_COLOR` is deliberately NOT a signal — it disables color,
|
|
125
|
+
* not glyphs.
|
|
126
|
+
*
|
|
127
|
+
* @returns {'unicode'|'ascii'}
|
|
128
|
+
*/
|
|
129
|
+
export function detectGlyphMode(env = process.env, platform = process.platform) {
|
|
130
|
+
const explicit = String(env.CARTHAGENT_GLYPHS || '').trim().toLowerCase();
|
|
131
|
+
if (explicit === 'ascii' || explicit === 'unicode') return explicit;
|
|
132
|
+
const flag = String(env.CARTHAGENT_ASCII || '').trim().toLowerCase();
|
|
133
|
+
if (flag === '1' || flag === 'true' || flag === 'yes') return 'ascii';
|
|
134
|
+
if (flag === '0' || flag === 'false' || flag === 'no') return 'unicode';
|
|
135
|
+
|
|
136
|
+
// A non-UTF-8 locale or a dumb terminal can't carry the symbol set at all.
|
|
137
|
+
const locale = String(env.LC_ALL || env.LC_CTYPE || env.LANG || '');
|
|
138
|
+
const utf8 = !locale || /utf-?8/i.test(locale);
|
|
139
|
+
if (!utf8) return 'ascii';
|
|
140
|
+
if (env.TERM === 'dumb') return 'ascii';
|
|
141
|
+
|
|
142
|
+
// `auto` prefers ASCII unless we have positive evidence of a modern terminal
|
|
143
|
+
// emulator. Half the designed glyphs (Geometric Shapes, Braille, Misc
|
|
144
|
+
// Symbols) silently render as `?` in legacy consoles and limited fonts, and
|
|
145
|
+
// there is no reliable runtime font probe — so opt in to Unicode, not out.
|
|
146
|
+
// Set CARTHAGENT_GLYPHS=unicode / --glyphs unicode to force the designed set.
|
|
147
|
+
const marker = KNOWN_UTF8_TERMINALS.some(k => env[k]);
|
|
148
|
+
const truecolor = /truecolor|24bit/i.test(env.COLORTERM || '');
|
|
149
|
+
const modernTerm = /xterm|screen|tmux|linux|vt|ansi|alacritty|kitty|wezterm|ghostty/i.test(env.TERM || '');
|
|
150
|
+
if (marker || (truecolor && modernTerm)) return 'unicode';
|
|
151
|
+
return 'ascii';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Resolve a full glyph object for an option bag / env. */
|
|
155
|
+
export function resolveGlyphs({ mode, ascii, env = process.env, platform = process.platform } = {}) {
|
|
156
|
+
// Only explicit ascii/unicode win; `auto`, undefined and unknown values
|
|
157
|
+
// fall through to detection (so `--glyphs auto` behaves as documented).
|
|
158
|
+
const explicit = mode === 'ascii' || mode === 'unicode' ? mode : ascii ? 'ascii' : null;
|
|
159
|
+
const chosen = explicit || detectGlyphMode(env, platform);
|
|
160
|
+
return chosen === 'ascii' ? ASCII_GLYPHS : UNICODE_GLYPHS;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Spinner frame for a resolved set. */
|
|
164
|
+
export function spinnerFrame(glyphs, frame) {
|
|
165
|
+
const s = glyphs?.spinner || UNICODE_GLYPHS.spinner;
|
|
166
|
+
return s[((frame % s.length) + s.length) % s.length];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** True when a resolved set is the ASCII fallback tier. */
|
|
170
|
+
export function isAsciiGlyphs(glyphs) {
|
|
171
|
+
return glyphs === ASCII_GLYPHS;
|
|
172
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Worker thread: compute the workspace fingerprint off the UI thread. */
|
|
2
|
+
import { parentPort } from 'node:worker_threads';
|
|
3
|
+
import { fingerprint } from '../delivery.mjs';
|
|
4
|
+
|
|
5
|
+
parentPort.on('message', ({ cwd, roots }) => {
|
|
6
|
+
try {
|
|
7
|
+
parentPort.postMessage({ hash: fingerprint(cwd, roots) });
|
|
8
|
+
} catch (err) {
|
|
9
|
+
parentPort.postMessage({ error: String(err.message || err) });
|
|
10
|
+
}
|
|
11
|
+
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mock the engine's `--mode rpc` for `carthagent demo`: speaks the rpc line protocol and plays a
|
|
4
|
+
* scripted end-to-end delivery (plan → build → check → finish) so the console's
|
|
5
|
+
* visuals can be exercised without a provider or API credit.
|
|
6
|
+
*
|
|
7
|
+
* It also writes real .harness/<session>/<run>/report.json + plan.d2 files, so
|
|
8
|
+
* the side panel exercises the same file-watching path as a live run.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { randomUUID } from 'node:crypto';
|
|
13
|
+
import { planD2, fingerprint } from '../delivery.mjs';
|
|
14
|
+
|
|
15
|
+
const SPEED = Math.max(0.05, Number(process.env.CARTHAGENT_DEMO_SPEED) || 1);
|
|
16
|
+
const START_DELAY = Math.max(0, Number(process.env.CARTHAGENT_DEMO_START_DELAY) || 0);
|
|
17
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms * SPEED));
|
|
18
|
+
const emit = obj => process.stdout.write(JSON.stringify(obj) + '\n');
|
|
19
|
+
|
|
20
|
+
const SESSION_ID = 'demo';
|
|
21
|
+
const cwd = process.cwd();
|
|
22
|
+
const runId = randomUUID();
|
|
23
|
+
const harnessDir = join(cwd, '.harness', SESSION_ID, runId);
|
|
24
|
+
|
|
25
|
+
let activeModel = { provider: 'demo', id: 'carthagent-demo-1' };
|
|
26
|
+
let thinkingLevel = 'medium';
|
|
27
|
+
const stats = { tokens: 0, cost: 0, toolCalls: 0, userMessages: 0, streaming: false, compacting: false, autoCompact: true, ctx: 9000 };
|
|
28
|
+
let aborted = false;
|
|
29
|
+
|
|
30
|
+
const usage = (input, output) => {
|
|
31
|
+
const totalTokens = input + output;
|
|
32
|
+
stats.tokens += totalTokens;
|
|
33
|
+
stats.cost += totalTokens * 1.2e-6;
|
|
34
|
+
stats.ctx = Math.min(190000, stats.ctx + Math.round(totalTokens * 0.8));
|
|
35
|
+
return { totalTokens, input, output, cost: { total: totalTokens * 1.2e-6 } };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function taskSummary(prompt) {
|
|
39
|
+
// The console wraps tasks in the delivery template; recover the user's text.
|
|
40
|
+
const m = String(prompt).match(/^Deliver this task in phases(?: with executable evidence)?:\s*([\s\S]*?)\n\n/);
|
|
41
|
+
const clean = (m ? m[1] : String(prompt)).replace(/\s+/g, ' ').trim();
|
|
42
|
+
return clean.length > 64 ? clean.slice(0, 63) + '…' : clean || 'demo task';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function demoPlan(prompt) {
|
|
46
|
+
return {
|
|
47
|
+
goal: taskSummary(prompt),
|
|
48
|
+
assumptions: ['standard library only', 'tests run in a fresh subprocess'],
|
|
49
|
+
artifacts: ['.'],
|
|
50
|
+
steps: ['Inspect workspace and probe runtime', 'Implement runnable vertical slice', 'Cover edge cases, tests and docs'],
|
|
51
|
+
acceptance: [
|
|
52
|
+
{ requirement: 'Core behavior works in a fresh process', checks: ['smoke'] },
|
|
53
|
+
{ requirement: 'Edge cases and persistence covered by tests', checks: ['unit'] },
|
|
54
|
+
],
|
|
55
|
+
checks: [
|
|
56
|
+
{ id: 'unit', kind: 'test', argv: ['node', '--test', 'tests/app.test.mjs'], timeoutSeconds: 60 },
|
|
57
|
+
{ id: 'smoke', kind: 'runtime', argv: ['node', 'app.mjs', '--smoke'], timeoutSeconds: 30 },
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function writeReport(state) {
|
|
63
|
+
mkdirSync(harnessDir, { recursive: true });
|
|
64
|
+
writeFileSync(join(harnessDir, 'report.json'), JSON.stringify(state, null, 2) + '\n');
|
|
65
|
+
writeFileSync(join(harnessDir, 'plan.d2'), planD2(state.plan));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function toolCall(id, name, args, result, { update, ms = 600 } = {}) {
|
|
69
|
+
emit({ type: 'message_update', assistantMessageEvent: { type: 'toolcall_start', contentIndex: 1, id, toolName: name } });
|
|
70
|
+
stats.toolCalls++;
|
|
71
|
+
emit({ type: 'tool_execution_start', toolCallId: id, toolName: name, args });
|
|
72
|
+
if (update) for (const u of update) { await sleep(320); emit({ type: 'tool_execution_update', toolCallId: id, partialResult: { content: [{ type: 'text', text: u }] } }); }
|
|
73
|
+
await sleep(ms);
|
|
74
|
+
emit({ type: 'tool_execution_end', toolCallId: id, toolName: name, isError: false, result: { content: [{ type: 'text', text: result }] } });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function assistantText(text, { thinking } = {}) {
|
|
78
|
+
emit({ type: 'message_start', message: { role: 'assistant', content: [] } });
|
|
79
|
+
if (thinking) {
|
|
80
|
+
for (const chunk of thinking.match(/.{1,24}/gs) || []) {
|
|
81
|
+
emit({ type: 'message_update', assistantMessageEvent: { type: 'thinking_delta', contentIndex: 0, delta: chunk } });
|
|
82
|
+
await sleep(60);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const chunk of text.match(/.{1,28}/gs) || []) {
|
|
86
|
+
emit({ type: 'message_update', assistantMessageEvent: { type: 'text_delta', contentIndex: 0, delta: chunk } });
|
|
87
|
+
await sleep(45);
|
|
88
|
+
}
|
|
89
|
+
emit({ type: 'message_end', message: { role: 'assistant', content: [{ type: 'text', text }], usage: usage(4200, 900), stopReason: 'stop' } });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function runDemo(prompt) {
|
|
93
|
+
const plan = demoPlan(prompt);
|
|
94
|
+
const state = { version: 1, runId, plan, evidence: {}, status: 'implementing', createdAt: new Date().toISOString() };
|
|
95
|
+
stats.streaming = true; aborted = false;
|
|
96
|
+
if (START_DELAY) await new Promise(r => setTimeout(r, START_DELAY));
|
|
97
|
+
emit({ type: 'agent_start' });
|
|
98
|
+
emit({ type: 'message_start', message: { role: 'user', content: [{ type: 'text', text: prompt }] } });
|
|
99
|
+
emit({ type: 'message_end', message: { role: 'user', content: [{ type: 'text', text: prompt }] } });
|
|
100
|
+
await sleep(250);
|
|
101
|
+
|
|
102
|
+
emit({ type: 'turn_start' });
|
|
103
|
+
await assistantText('I’ll inspect the workspace, define the checks, and implement the task.', {
|
|
104
|
+
thinking: 'Mapping the request to a small acceptance contract.',
|
|
105
|
+
});
|
|
106
|
+
if (aborted) return endRun();
|
|
107
|
+
|
|
108
|
+
await toolCall('t1', 'delivery_plan', plan, JSON.stringify({ plan: join(harnessDir, 'plan.d2'), report: join(harnessDir, 'report.json'), next: 'Build a runnable slice, add regression tests, then delivery_check each check ID.' }, null, 2));
|
|
109
|
+
writeReport(state);
|
|
110
|
+
await sleep(300);
|
|
111
|
+
|
|
112
|
+
emit({ type: 'turn_start' });
|
|
113
|
+
await toolCall('t2', 'ls', { path: '.' }, 'app.mjs\ntests/\nREADME.md', { ms: 350 });
|
|
114
|
+
await toolCall('t3', 'write', { path: 'app.mjs', content: '// demo' }, 'Wrote app.mjs (86 lines)', { ms: 500 });
|
|
115
|
+
if (aborted) return endRun();
|
|
116
|
+
await toolCall('t4', 'write', { path: 'tests/app.test.mjs', content: '// demo' }, 'Wrote tests/app.test.mjs (41 lines)', { ms: 500 });
|
|
117
|
+
await assistantText('The implementation is ready. I’m exercising the entry point before running the declared checks.', { thinking: 'Verify the runnable path end to end, not just imports.' });
|
|
118
|
+
|
|
119
|
+
emit({ type: 'turn_start' });
|
|
120
|
+
await toolCall('t5', 'bash', { command: 'node app.mjs --smoke' }, 'SMOKE ok · 3 interactions · exit 0', { ms: 700 });
|
|
121
|
+
if (aborted) return endRun();
|
|
122
|
+
|
|
123
|
+
state.status = 'verifying';
|
|
124
|
+
writeReport(state);
|
|
125
|
+
let fp;
|
|
126
|
+
try { fp = fingerprint(cwd, ['.']); } catch { fp = 'f'.repeat(64); }
|
|
127
|
+
await toolCall('t6', 'delivery_check', { id: 'all' }, JSON.stringify([
|
|
128
|
+
{ id: 'unit', passed: true, code: 0, durationMs: 812 },
|
|
129
|
+
{ id: 'smoke', passed: true, code: 0, durationMs: 1330 },
|
|
130
|
+
], null, 2), {
|
|
131
|
+
update: ['Running unit: ["node","--test","tests/app.test.mjs"]', 'Running smoke: ["node","app.mjs","--smoke"]'],
|
|
132
|
+
ms: 400,
|
|
133
|
+
});
|
|
134
|
+
for (const [id, ms] of [['unit', 812], ['smoke', 1330]]) {
|
|
135
|
+
state.evidence[id] = { passed: true, fingerprint: fp, code: 0, durationMs: ms, outputTail: `${id} passed` };
|
|
136
|
+
}
|
|
137
|
+
writeReport(state);
|
|
138
|
+
await sleep(400);
|
|
139
|
+
|
|
140
|
+
emit({ type: 'turn_start' });
|
|
141
|
+
await assistantText('The checks pass on the current fingerprint. I’m reviewing scope before finishing.');
|
|
142
|
+
await toolCall('t7', 'delivery_finish', { status: 'verified', review: 'Fresh-process smoke plus unit coverage pass; scope matches the request.', launch: 'node app.mjs', limitations: ['demo evidence is scripted'] },
|
|
143
|
+
JSON.stringify({ status: 'verified', report: join(harnessDir, 'report.json'), note: 'Evidence covers declared checks, not a guarantee of correctness.' }, null, 2));
|
|
144
|
+
state.status = 'verified';
|
|
145
|
+
state.handoff = { status: 'verified', review: 'Fresh-process smoke plus unit coverage pass.', launch: 'node app.mjs', limitations: ['demo evidence is scripted'], fingerprint: fp, at: new Date().toISOString() };
|
|
146
|
+
writeReport(state);
|
|
147
|
+
await assistantText('Verified. Run with `node app.mjs`.');
|
|
148
|
+
endRun();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function endRun() {
|
|
152
|
+
stats.streaming = false;
|
|
153
|
+
emit({ type: 'agent_end', messages: [] });
|
|
154
|
+
emit({ type: 'agent_settled' });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function respond(cmd) {
|
|
158
|
+
const reply = data => emit({ type: 'response', id: cmd.id, success: true, data });
|
|
159
|
+
switch (cmd.type) {
|
|
160
|
+
case 'get_state':
|
|
161
|
+
return reply({
|
|
162
|
+
model: activeModel, thinkingLevel,
|
|
163
|
+
isStreaming: stats.streaming, isCompacting: stats.compacting,
|
|
164
|
+
sessionId: SESSION_ID, sessionName: 'demo', messageCount: stats.userMessages * 2,
|
|
165
|
+
pendingMessageCount: 0, steeringMode: 'all', followUpMode: 'one-at-a-time', autoCompactionEnabled: stats.autoCompact,
|
|
166
|
+
});
|
|
167
|
+
case 'get_session_stats':
|
|
168
|
+
return reply({
|
|
169
|
+
sessionId: SESSION_ID, userMessages: stats.userMessages, assistantMessages: stats.userMessages * 3,
|
|
170
|
+
toolCalls: stats.toolCalls, toolResults: stats.toolCalls, totalMessages: stats.userMessages * 4,
|
|
171
|
+
tokens: { input: Math.round(stats.tokens * 0.7), output: Math.round(stats.tokens * 0.3), cacheRead: 0, cacheWrite: 0, total: stats.tokens },
|
|
172
|
+
cost: stats.cost,
|
|
173
|
+
contextUsage: { tokens: stats.ctx, contextWindow: 200000, percent: Math.round(stats.ctx / 2000) },
|
|
174
|
+
});
|
|
175
|
+
case 'prompt':
|
|
176
|
+
stats.userMessages++;
|
|
177
|
+
reply({});
|
|
178
|
+
if (stats.streaming) return;
|
|
179
|
+
runDemo(cmd.message || '').catch(() => endRun());
|
|
180
|
+
return;
|
|
181
|
+
case 'steer':
|
|
182
|
+
reply({});
|
|
183
|
+
emit({ type: 'message_start', message: { role: 'user', content: [{ type: 'text', text: cmd.message || '' }] } });
|
|
184
|
+
emit({ type: 'message_end', message: { role: 'user', content: [{ type: 'text', text: cmd.message || '' }] } });
|
|
185
|
+
return;
|
|
186
|
+
case 'bash':
|
|
187
|
+
return reply({ exitCode: 0, output: `demo$ ${cmd.command || ''}\n(mock shell — no real execution)` });
|
|
188
|
+
case 'abort': aborted = true; return reply({});
|
|
189
|
+
case 'cycle_model': return reply({ model: { provider: 'demo', id: 'carthagent-demo-1' } });
|
|
190
|
+
case 'set_model': activeModel = { provider: cmd.provider, id: cmd.modelId }; return reply(activeModel);
|
|
191
|
+
case 'get_available_thinking_levels': return reply({ levels: ['off', 'low', 'medium', 'high'] });
|
|
192
|
+
case 'set_thinking_level': thinkingLevel = cmd.level; return reply({});
|
|
193
|
+
case 'get_available_models': return reply({ models: [
|
|
194
|
+
{ provider: 'demo', id: 'carthagent-demo-1' },
|
|
195
|
+
{ provider: 'demo', id: 'carthagent-demo-2' },
|
|
196
|
+
{ provider: 'openrouter', id: 'google/gemini-flash-latest' },
|
|
197
|
+
{ provider: 'openrouter', id: 'anthropic/claude-sonnet-latest' },
|
|
198
|
+
{ provider: 'openrouter', id: 'openai/gpt-latest' },
|
|
199
|
+
{ provider: 'openrouter', id: 'deepseek/deepseek-v4.1-flash', name: 'DeepSeek: DeepSeek V4.1 Flash' },
|
|
200
|
+
] });
|
|
201
|
+
case 'cycle_thinking_level': return reply({ level: 'high' });
|
|
202
|
+
case 'compact': stats.compacting = true; reply({}); setTimeout(() => { stats.compacting = false; stats.ctx = 9000; }, 900); return;
|
|
203
|
+
case 'set_auto_compaction': stats.autoCompact = Boolean(cmd.enabled); return reply({});
|
|
204
|
+
case 'new_session': stats.userMessages = 0; stats.tokens = 0; stats.cost = 0; stats.ctx = 9000; return reply({ cancelled: false });
|
|
205
|
+
case 'export_html': return reply({ path: join(cwd, 'demo-export.html') });
|
|
206
|
+
case 'extension_ui_response': return;
|
|
207
|
+
default: return reply({});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let buf = '';
|
|
212
|
+
process.stdin.on('data', d => {
|
|
213
|
+
buf += d.toString();
|
|
214
|
+
let i;
|
|
215
|
+
while ((i = buf.indexOf('\n')) !== -1) {
|
|
216
|
+
const line = buf.slice(0, i); buf = buf.slice(i + 1);
|
|
217
|
+
if (!line.trim()) continue;
|
|
218
|
+
let cmd; try { cmd = JSON.parse(line); } catch { continue; }
|
|
219
|
+
respond(cmd);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
process.stdin.on('end', () => process.exit(0));
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for the console's provider/model picker.
|
|
3
|
+
*
|
|
4
|
+
* `state.modelList` is a flat array of `{ provider, id, full }` from the engine's
|
|
5
|
+
* `get_available_models`. The settings overlay groups/filters it by provider
|
|
6
|
+
* so multiple authenticated providers can coexist and be switched between.
|
|
7
|
+
* Provider ids remain internal routing identities; user-facing surfaces map the
|
|
8
|
+
* managed provider to the Carthagent Ship product name.
|
|
9
|
+
*/
|
|
10
|
+
import { providerDisplayName } from '../providers/names.mjs';
|
|
11
|
+
|
|
12
|
+
/** Sorted unique provider ids present in a model list. */
|
|
13
|
+
export function providersFromModels(list) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
for (const m of list || []) if (m?.provider) seen.add(m.provider);
|
|
16
|
+
return [...seen].sort((a, b) => a.localeCompare(b));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Provider id for the current model, resolved against the available list.
|
|
20
|
+
* Falls back to the `provider/…` prefix when the model isn't listed yet. */
|
|
21
|
+
export function providerForModel(model, list) {
|
|
22
|
+
const s = String(model || '');
|
|
23
|
+
const hit = (list || []).find(m => m.full === s) || (list || []).find(m => m.id === s);
|
|
24
|
+
if (hit) return hit.provider;
|
|
25
|
+
const slash = s.indexOf('/');
|
|
26
|
+
return slash > 0 ? s.slice(0, slash) : '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Filter a model list by provider and (optionally) a search string, capped. */
|
|
30
|
+
export function modelMatchesQuery(model, query) {
|
|
31
|
+
const tokens = String(query || '').toLowerCase().match(/[\p{L}\p{N}]+/gu) || [];
|
|
32
|
+
if (!tokens.length) return true;
|
|
33
|
+
const searchable = [model?.provider, providerDisplayName(model?.provider), model?.id, model?.full, model?.name]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(' ')
|
|
36
|
+
.toLowerCase();
|
|
37
|
+
return tokens.every(token => searchable.includes(token));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function filterModelsByProvider(list, provider, query, limit = 50) {
|
|
41
|
+
let out = list || [];
|
|
42
|
+
if (provider) out = out.filter(m => m.provider === provider);
|
|
43
|
+
if (query) out = out.filter(m => modelMatchesQuery(m, query));
|
|
44
|
+
return out.slice(0, limit);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** True when the engine resolved the model as reasoning-capable. */
|
|
48
|
+
export function modelSupportsThinking(model) {
|
|
49
|
+
return model?.reasoning === true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Next provider id when cycling a list, wrapping in both directions. */
|
|
53
|
+
export function cycleProvider(providers, current, dir) {
|
|
54
|
+
if (!providers?.length) return '';
|
|
55
|
+
const i = providers.indexOf(current);
|
|
56
|
+
const at = i === -1 ? 0 : i;
|
|
57
|
+
return providers[(at + dir + providers.length) % providers.length];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Coalesce RPC reads, but never retain a startup snapshot indefinitely. */
|
|
61
|
+
export function createModelLoader(rpc, state) {
|
|
62
|
+
let pending;
|
|
63
|
+
return () => {
|
|
64
|
+
if (pending) return pending;
|
|
65
|
+
pending = rpc.send({ type: 'get_available_models' }).then(response => {
|
|
66
|
+
if (!Array.isArray(response?.models)) return state.modelList || [];
|
|
67
|
+
const list = response.models.map(model => ({ ...model, name: model.name || '', full: `${model.provider}/${model.id}` }));
|
|
68
|
+
state.modelList = list;
|
|
69
|
+
return list;
|
|
70
|
+
}).catch(() => state.modelList || []).finally(() => { pending = null; });
|
|
71
|
+
return pending;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Shared keyboard semantics: navigation is never a commit. */
|
|
2
|
+
export function pickerKey(picker, key, count) {
|
|
3
|
+
if (picker.pending) return 'pending';
|
|
4
|
+
const moves = { up: -1, down: 1, pgup: -8, pgdn: 8 };
|
|
5
|
+
if (key in moves) picker.sel = Math.max(0, Math.min(Math.max(0, count - 1), picker.sel + moves[key]));
|
|
6
|
+
if (key === 'esc') return 'cancel';
|
|
7
|
+
if (key === 'enter' && count) return 'commit';
|
|
8
|
+
return 'browse';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function commitPicker(picker, action, success) {
|
|
12
|
+
if (picker.pending) return;
|
|
13
|
+
picker.pending = true;
|
|
14
|
+
picker.error = '';
|
|
15
|
+
try { await action(); success(); }
|
|
16
|
+
catch (error) { picker.error = error.message; }
|
|
17
|
+
finally { picker.pending = false; }
|
|
18
|
+
}
|