mixdog 0.9.3 → 0.9.4
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/package.json +7 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +23 -63
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +251 -397
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +32 -41
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +84 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +945 -8094
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +23 -101
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +19652 -18630
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +67 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
package/scripts/tool-smoke.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { buildHeadlessSpawnArgs } from '../src/headless-role.mjs';
|
|
|
11
11
|
import { createStandaloneChannelWorker } from '../src/standalone/channel-worker.mjs';
|
|
12
12
|
import { OpenAIOAuthProvider, buildRequestBody, sendViaHttpSse } from '../src/runtime/agent/orchestrator/providers/openai-oauth.mjs';
|
|
13
13
|
import { _test as _anthropicOAuthTest } from '../src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs';
|
|
14
|
-
import { _logicalResponseItemMatch
|
|
14
|
+
import { _logicalResponseItemMatch } from '../src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs';
|
|
15
15
|
import { _mergePendingMessageEntries, applyAskTerminalUsageTotals, closeSession, createSession, drainPendingMessages, enqueuePendingMessage, resumeSession } from '../src/runtime/agent/orchestrator/session/manager.mjs';
|
|
16
16
|
import {
|
|
17
17
|
contentHasImage,
|
|
@@ -137,46 +137,6 @@ function assertOk(name, result, pattern = null) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
{
|
|
140
|
-
const fullPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
141
|
-
mode: 'full',
|
|
142
|
-
frameInputItems: 40,
|
|
143
|
-
deltaTokens: 9000,
|
|
144
|
-
hasPreviousResponseId: false,
|
|
145
|
-
});
|
|
146
|
-
assert(fullPolicy.policy === 'full_guard' && fullPolicy.limitPerMin === 12, `full cache lane should keep 12rpm guard: ${JSON.stringify(fullPolicy)}`);
|
|
147
|
-
|
|
148
|
-
const deltaPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
149
|
-
mode: 'delta',
|
|
150
|
-
frameInputItems: 1,
|
|
151
|
-
deltaTokens: 9000,
|
|
152
|
-
hasPreviousResponseId: true,
|
|
153
|
-
});
|
|
154
|
-
assert(deltaPolicy.policy === 'delta_relaxed' && deltaPolicy.limitPerMin === 60, `small delta should use relaxed cache lane rpm: ${JSON.stringify(deltaPolicy)}`);
|
|
155
|
-
|
|
156
|
-
const largeDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
157
|
-
mode: 'delta',
|
|
158
|
-
frameInputItems: 20,
|
|
159
|
-
deltaTokens: 9000,
|
|
160
|
-
hasPreviousResponseId: true,
|
|
161
|
-
});
|
|
162
|
-
assert(largeDeltaPolicy.policy === 'delta_guarded' && largeDeltaPolicy.limitPerMin === 12, `large delta should fall back to full guard: ${JSON.stringify(largeDeltaPolicy)}`);
|
|
163
|
-
|
|
164
|
-
const customDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({ openaiCacheLaneDeltaRateLimitPerMin: 90 }, {
|
|
165
|
-
mode: 'delta',
|
|
166
|
-
frameInputItems: 1,
|
|
167
|
-
deltaTokens: 9000,
|
|
168
|
-
hasPreviousResponseId: true,
|
|
169
|
-
});
|
|
170
|
-
assert(customDeltaPolicy.limitPerMin === 90, `custom delta rpm should be honored: ${JSON.stringify(customDeltaPolicy)}`);
|
|
171
|
-
|
|
172
|
-
const unlimitedDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({ openaiCacheLaneDeltaRateLimitPerMin: 0 }, {
|
|
173
|
-
mode: 'delta',
|
|
174
|
-
frameInputItems: 1,
|
|
175
|
-
deltaTokens: 9000,
|
|
176
|
-
hasPreviousResponseId: true,
|
|
177
|
-
});
|
|
178
|
-
assert(unlimitedDeltaPolicy.policy === 'delta_unlimited' && unlimitedDeltaPolicy.limitPerMin === 0, `delta rpm=0 should disable delta rate wait: ${JSON.stringify(unlimitedDeltaPolicy)}`);
|
|
179
|
-
|
|
180
140
|
const originalFunctionCall = {
|
|
181
141
|
type: 'function_call',
|
|
182
142
|
call_id: 'call_tool_1',
|
|
@@ -589,20 +549,28 @@ if (grepContextPolicyArgs['-C'] !== GREP_CONTEXT_MAX || Object.prototype.hasOwnP
|
|
|
589
549
|
throw new Error(`grep context policy must canonicalize and clamp explicit context: ${JSON.stringify(grepContextPolicyArgs)}`);
|
|
590
550
|
}
|
|
591
551
|
|
|
592
|
-
|
|
552
|
+
// Multiple absolute paths in one string are now auto-split into a path array
|
|
553
|
+
// (arg-guard splitMultipleAbsoluteWindowsPaths) instead of rejected.
|
|
554
|
+
const multiGrepPathArgs = {
|
|
593
555
|
pattern: 'providerStatus',
|
|
594
556
|
path: 'C:\\Project\\mixdog\\src\\tui C:\\Project\\mixdog\\src\\mixdog-session-runtime.mjs',
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
557
|
+
};
|
|
558
|
+
const multiGrepPathErr = validateBuiltinArgs('grep', multiGrepPathArgs);
|
|
559
|
+
if (multiGrepPathErr) {
|
|
560
|
+
throw new Error(`grep multi-path auto-split should pass validation: ${multiGrepPathErr}`);
|
|
561
|
+
}
|
|
562
|
+
if (!Array.isArray(multiGrepPathArgs.path) || multiGrepPathArgs.path.length !== 2) {
|
|
563
|
+
throw new Error(`grep multi-path auto-split should coerce to 2-element array: ${JSON.stringify(multiGrepPathArgs.path)}`);
|
|
598
564
|
}
|
|
599
565
|
|
|
600
|
-
|
|
566
|
+
// Lookaround/backrefs are no longer rejected at validation time: search-tool
|
|
567
|
+
// routes them to rg --pcre2 at runtime (arg-guard.mjs comment near globKeys).
|
|
568
|
+
const lookaroundGrepErr = validateBuiltinArgs('grep', {
|
|
601
569
|
pattern: 'C:\\\\Project(?!\\\\mixdog)',
|
|
602
570
|
path: root,
|
|
603
571
|
});
|
|
604
|
-
if (
|
|
605
|
-
throw new Error(`grep
|
|
572
|
+
if (lookaroundGrepErr) {
|
|
573
|
+
throw new Error(`grep lookaround pattern should pass validation (PCRE2 runtime routing): ${lookaroundGrepErr}`);
|
|
606
574
|
}
|
|
607
575
|
|
|
608
576
|
// Windows drive path + no explicit shell used to be rejected with a retry hint;
|
|
@@ -991,10 +959,7 @@ setInternalToolsProvider({
|
|
|
991
959
|
{ name: 'recall', description: 'Memory recall surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true } },
|
|
992
960
|
{ name: 'search', description: 'Web search surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true, openWorldHint: true } },
|
|
993
961
|
{ name: 'reply', description: 'Channel reply surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
994
|
-
{ name: 'edit_message', description: 'Channel edit surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
995
962
|
{ name: 'web_fetch', description: 'Web fetch surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true, openWorldHint: true } },
|
|
996
|
-
{ name: 'reload_config', description: 'Config reload surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
997
|
-
{ name: 'inject_command', description: 'Command injection surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
998
963
|
],
|
|
999
964
|
});
|
|
1000
965
|
{
|
|
@@ -1197,12 +1162,12 @@ setInternalToolsProvider({
|
|
|
1197
1162
|
if (readTools.includes('shell') || writeTools.includes('shell')) {
|
|
1198
1163
|
throw new Error(`read/read-write agent schemas must omit shell: read=${readTools.join(', ')} write=${writeTools.join(', ')}`);
|
|
1199
1164
|
}
|
|
1200
|
-
for (const name of ['shell', 'apply_patch', 'task'
|
|
1165
|
+
for (const name of ['shell', 'apply_patch', 'task']) {
|
|
1201
1166
|
if (readTools.includes(name)) {
|
|
1202
1167
|
throw new Error(`read agent schema must omit non-read tool ${name}: read=${readTools.join(', ')}`);
|
|
1203
1168
|
}
|
|
1204
1169
|
}
|
|
1205
|
-
for (const name of ['apply_patch', 'task'
|
|
1170
|
+
for (const name of ['apply_patch', 'task']) {
|
|
1206
1171
|
if (name === 'apply_patch' && !writeTools.includes(name)) {
|
|
1207
1172
|
throw new Error(`read-write agent schema must preserve apply_patch: write=${writeTools.join(', ')}`);
|
|
1208
1173
|
}
|
|
@@ -1210,12 +1175,7 @@ setInternalToolsProvider({
|
|
|
1210
1175
|
throw new Error(`read-write agent schema must omit non-edit tool ${name}: write=${writeTools.join(', ')}`);
|
|
1211
1176
|
}
|
|
1212
1177
|
}
|
|
1213
|
-
for (const name of ['
|
|
1214
|
-
if (writeTools.includes(name)) {
|
|
1215
|
-
throw new Error(`read-write agent schema must omit config/skill tool ${name}: write=${writeTools.join(', ')}`);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
for (const name of ['memory', 'recall', 'reply', 'edit_message', 'reload_config', 'inject_command']) {
|
|
1178
|
+
for (const name of ['memory', 'recall', 'reply']) {
|
|
1219
1179
|
if (readTools.includes(name) || writeTools.includes(name)) {
|
|
1220
1180
|
throw new Error(`read/read-write agent schema must not expose full-runtime internal tool ${name}: read=${readTools.join(', ')} write=${writeTools.join(', ')}`);
|
|
1221
1181
|
}
|
|
@@ -1293,7 +1253,7 @@ setInternalToolsProvider({
|
|
|
1293
1253
|
const hiddenAgents = JSON.parse(readFileSync(join(root, 'src', 'defaults', 'agents.json'), 'utf8')).agents || [];
|
|
1294
1254
|
const hiddenPreset = { id: 'hidden-smoke', name: 'hidden-smoke', type: 'agent', provider: 'openai-oauth', model: 'tool-smoke-model', tools: 'full' };
|
|
1295
1255
|
const hiddenRuntimeSpec = { scopeKey: 'hidden-role-smoke', lane: 'agent' };
|
|
1296
|
-
const hiddenBadTools = new Set(['shell', 'task', '
|
|
1256
|
+
const hiddenBadTools = new Set(['shell', 'task', 'Skill', 'memory', 'reply', 'recall']);
|
|
1297
1257
|
const expectedForHiddenAgent = (permission, schemaAllowedTools) => {
|
|
1298
1258
|
if (Array.isArray(schemaAllowedTools)) return schemaAllowedTools.slice();
|
|
1299
1259
|
if (permission === 'none') return [];
|
|
@@ -1517,7 +1477,7 @@ if (!/before text grep/i.test(codeGraphDescription)) {
|
|
|
1517
1477
|
if (!/repo-local/i.test(codeGraphDescription) || !/NOT web search|not web/i.test(codeGraphDescription)) {
|
|
1518
1478
|
throw new Error('code_graph description must mark itself repo-local (not web search)');
|
|
1519
1479
|
}
|
|
1520
|
-
if (!/repo-local|not web/i.test(codeGraphProps.mode?.description || '') || !/source file/i.test(codeGraphProps.
|
|
1480
|
+
if (!/repo-local|not web/i.test(codeGraphProps.mode?.description || '') || !/source file/i.test(codeGraphProps.files?.description || '')) {
|
|
1521
1481
|
throw new Error('code_graph schema must keep compact, repo-local field descriptions');
|
|
1522
1482
|
}
|
|
1523
1483
|
const recallTool = MEMORY_TOOL_DEFS.find((tool) => tool.name === 'recall');
|
|
@@ -1588,7 +1548,7 @@ if (!recallProps.sessionOnly || !/session only/i.test(recallProps.sessionOnly?.d
|
|
|
1588
1548
|
}
|
|
1589
1549
|
const memoryTool = MEMORY_TOOL_DEFS.find((tool) => tool.name === 'memory');
|
|
1590
1550
|
const memoryProps = memoryTool?.inputSchema?.properties || {};
|
|
1591
|
-
if (!/
|
|
1551
|
+
if (!/mutation/i.test(memoryTool?.description || '') || !/Exact confirmation phrase/i.test(memoryProps.confirm?.description || '')) {
|
|
1592
1552
|
throw new Error('memory schema must preserve mutation/destructive confirmation guidance');
|
|
1593
1553
|
}
|
|
1594
1554
|
const searchTool = SEARCH_TOOL_DEFS.find((tool) => tool.name === 'search');
|
|
@@ -1783,7 +1743,7 @@ if (grepTool?.inputSchema?.properties?.head_limit?.minimum !== 0 || !/keep small
|
|
|
1783
1743
|
if (grepTool?.inputSchema?.properties?.type) {
|
|
1784
1744
|
throw new Error('grep type schema must stay hidden; prefer glob for extension narrowing');
|
|
1785
1745
|
}
|
|
1786
|
-
if (!/code flow before text search/i.test(codeGraphProps.mode?.description || '') || !/one anchor is enough/i.test(codeGraphProps.
|
|
1746
|
+
if (!/code flow before text search/i.test(codeGraphProps.mode?.description || '') || !/one anchor is enough/i.test(codeGraphProps.symbols?.description || '') || !/one call/i.test(codeGraphProps.symbols?.description || '')) {
|
|
1787
1747
|
throw new Error('code_graph schema fields must steer away from repeated lookup loops');
|
|
1788
1748
|
}
|
|
1789
1749
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* scripts/tui-render-smoke.mjs — undeclared-identifier gate for the TUI bundle.
|
|
4
|
+
*
|
|
5
|
+
* Why: esbuild does not flag free (undeclared) identifiers in JSX modules —
|
|
6
|
+
* they may legitimately be runtime globals — and no smoke actually renders
|
|
7
|
+
* App. That gap shipped two ReferenceErrors (promptContentRows,
|
|
8
|
+
* TRANSCRIPT_WINDOW_OVERSCAN_ROWS) during the App.jsx split.
|
|
9
|
+
*
|
|
10
|
+
* How: parse src/tui/dist/index.mjs with acorn, run eslint-scope, and fail on
|
|
11
|
+
* any reference that resolves to no binding and is not a known JS/Node/DOM
|
|
12
|
+
* global. Static, deterministic, no terminal or engine session needed.
|
|
13
|
+
*
|
|
14
|
+
* Run: node scripts/tui-render-smoke.mjs (or `npm run smoke:tui`)
|
|
15
|
+
* Exit: 0 = clean, 1 = undeclared identifiers found or bundle missing.
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
18
|
+
import { dirname, join } from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { parse } from 'acorn';
|
|
21
|
+
import { analyze } from 'eslint-scope';
|
|
22
|
+
|
|
23
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
24
|
+
const BUNDLE = join(ROOT, 'src', 'tui', 'dist', 'index.mjs');
|
|
25
|
+
|
|
26
|
+
// Globals that are legal free identifiers in the bundle. Anything outside
|
|
27
|
+
// this set that resolves to no binding is a guaranteed ReferenceError at
|
|
28
|
+
// runtime (module scope is strict; there is no window fallback in node ESM).
|
|
29
|
+
const KNOWN_GLOBALS = new Set([
|
|
30
|
+
// ECMAScript builtins
|
|
31
|
+
'Array', 'ArrayBuffer', 'Atomics', 'BigInt', 'BigInt64Array', 'BigUint64Array', 'Boolean',
|
|
32
|
+
'DataView', 'Date', 'Error', 'EvalError', 'FinalizationRegistry', 'Float32Array',
|
|
33
|
+
'Float64Array', 'Function', 'Infinity', 'Int8Array', 'Int16Array', 'Int32Array',
|
|
34
|
+
'Intl', 'JSON', 'Map', 'Math', 'NaN', 'Number', 'Object', 'Promise', 'Proxy',
|
|
35
|
+
'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set', 'SharedArrayBuffer',
|
|
36
|
+
'String', 'Symbol', 'SyntaxError', 'TypeError', 'URIError', 'Uint8Array',
|
|
37
|
+
'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap', 'WeakRef', 'WeakSet',
|
|
38
|
+
'decodeURI', 'decodeURIComponent', 'encodeURI', 'encodeURIComponent', 'eval',
|
|
39
|
+
'globalThis', 'isFinite', 'isNaN', 'parseFloat', 'parseInt', 'undefined',
|
|
40
|
+
// Node.js globals
|
|
41
|
+
'AbortController', 'AbortSignal', 'Blob', 'Buffer', 'ByteLengthQueuingStrategy',
|
|
42
|
+
'CompressionStream', 'CountQueuingStrategy', 'Crypto', 'CryptoKey', 'CustomEvent',
|
|
43
|
+
'DecompressionStream', 'Event', 'EventTarget', 'File', 'FormData', 'Headers',
|
|
44
|
+
'MessageChannel', 'MessageEvent', 'MessagePort', 'Navigator', 'PerformanceEntry',
|
|
45
|
+
'PerformanceObserver', 'ReadableStream', 'ReadableStreamDefaultReader', 'Request',
|
|
46
|
+
'Response', 'SubtleCrypto', 'TextDecoder', 'TextDecoderStream', 'TextEncoder',
|
|
47
|
+
'TextEncoderStream', 'TransformStream', 'URL', 'URLSearchParams', 'WebAssembly',
|
|
48
|
+
'WebSocket', 'WritableStream', 'atob', 'btoa', 'clearImmediate', 'clearInterval',
|
|
49
|
+
'clearTimeout', 'console', 'crypto', 'fetch', 'navigator', 'performance',
|
|
50
|
+
'process', 'queueMicrotask', 'require', 'setImmediate', 'setInterval',
|
|
51
|
+
'setTimeout', 'structuredClone',
|
|
52
|
+
// Bundler/runtime artifacts occasionally referenced defensively
|
|
53
|
+
'window', 'document', 'reportError',
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
if (!existsSync(BUNDLE)) {
|
|
57
|
+
console.error(`tui-render-smoke: FAIL — bundle not found at ${BUNDLE}; run \`npm run build:tui\` first`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const source = readFileSync(BUNDLE, 'utf8');
|
|
62
|
+
|
|
63
|
+
let ast;
|
|
64
|
+
try {
|
|
65
|
+
ast = parse(source, { ecmaVersion: 'latest', sourceType: 'module', locations: true, ranges: true });
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error(`tui-render-smoke: FAIL — bundle does not parse: ${err.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const scopeManager = analyze(ast, { ecmaVersion: 2022, sourceType: 'module' });
|
|
72
|
+
const unresolved = scopeManager.globalScope.through.filter(
|
|
73
|
+
(ref) => !KNOWN_GLOBALS.has(ref.identifier.name),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (unresolved.length > 0) {
|
|
77
|
+
const seen = new Map();
|
|
78
|
+
for (const ref of unresolved) {
|
|
79
|
+
const { name } = ref.identifier;
|
|
80
|
+
if (!seen.has(name)) seen.set(name, ref.identifier.loc?.start);
|
|
81
|
+
}
|
|
82
|
+
console.error(`tui-render-smoke: FAIL — ${seen.size} undeclared identifier(s) in src/tui/dist/index.mjs:`);
|
|
83
|
+
for (const [name, loc] of seen) {
|
|
84
|
+
console.error(` ${name} (dist/index.mjs:${loc ? `${loc.line}:${loc.column}` : '?'})`);
|
|
85
|
+
}
|
|
86
|
+
console.error('These throw ReferenceError when the TUI renders. Fix missing imports in src/tui/ and rebuild.');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('tui-render-smoke: ok — no undeclared identifiers in TUI bundle');
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import * as crypto from 'node:crypto';
|
|
4
|
+
import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { readMarkdownDocument } from '../src/runtime/shared/markdown-frontmatter.mjs';
|
|
8
|
+
|
|
9
|
+
// DATA_DIR resolves from MIXDOG_DATA_DIR at module import time (via
|
|
10
|
+
// ./config.mjs -> resolvePluginData), so the env var MUST be set before
|
|
11
|
+
// the first dynamic import of webhook.mjs.
|
|
12
|
+
const dataDir = mkdtempSync(join(tmpdir(), 'mixdog-webhook-test-'));
|
|
13
|
+
process.env.MIXDOG_DATA_DIR = dataDir;
|
|
14
|
+
|
|
15
|
+
const {
|
|
16
|
+
extractSignature,
|
|
17
|
+
verifySignature,
|
|
18
|
+
loadEndpointConfig,
|
|
19
|
+
STRIPE_TOLERANCE_MS,
|
|
20
|
+
} = await import('../src/runtime/channels/lib/webhook.mjs');
|
|
21
|
+
|
|
22
|
+
function hmacHex(secret, payload) {
|
|
23
|
+
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── verifySignature: github/generic hex HMAC ──────────────────────────
|
|
27
|
+
|
|
28
|
+
test('verifySignature: github parser accepts a valid sha256 hex signature', () => {
|
|
29
|
+
const secret = 'shh';
|
|
30
|
+
const body = '{"hello":"world"}';
|
|
31
|
+
const sig = hmacHex(secret, body);
|
|
32
|
+
assert.equal(verifySignature(secret, body, sig, 'github'), true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('verifySignature: generic parser accepts a valid sha256 hex signature', () => {
|
|
36
|
+
const secret = 'shh';
|
|
37
|
+
const body = '{"hello":"world"}';
|
|
38
|
+
const sig = hmacHex(secret, body);
|
|
39
|
+
assert.equal(verifySignature(secret, body, sig, 'generic'), true);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('verifySignature: github/generic reject a wrong signature', () => {
|
|
43
|
+
const secret = 'shh';
|
|
44
|
+
const body = '{"hello":"world"}';
|
|
45
|
+
const wrong = hmacHex('other-secret', body);
|
|
46
|
+
assert.equal(verifySignature(secret, body, wrong, 'github'), false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('verifySignature: length-mismatch hex signature returns false without throwing', () => {
|
|
50
|
+
const secret = 'shh';
|
|
51
|
+
const body = 'payload';
|
|
52
|
+
// Valid hex, wrong byte length vs the 32-byte sha256 digest.
|
|
53
|
+
assert.doesNotThrow(() => {
|
|
54
|
+
assert.equal(verifySignature(secret, body, 'ab', 'github'), false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('verifySignature: malformed (non-hex) signature returns false without throwing', () => {
|
|
59
|
+
const secret = 'shh';
|
|
60
|
+
const body = 'payload';
|
|
61
|
+
assert.doesNotThrow(() => {
|
|
62
|
+
assert.equal(verifySignature(secret, body, 'not-hex-zzzz', 'github'), false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ── verifySignature: stripe t= / v1= scheme ───────────────────────────
|
|
67
|
+
|
|
68
|
+
test('verifySignature: stripe accepts a valid t+v1 pair', () => {
|
|
69
|
+
const secret = 'stripe-secret';
|
|
70
|
+
const body = '{"id":"evt_1"}';
|
|
71
|
+
const t = Math.floor(Date.now() / 1000);
|
|
72
|
+
const v1 = hmacHex(secret, `${t}.${body}`);
|
|
73
|
+
assert.equal(verifySignature(secret, body, `t=${t},v1=${v1}`, 'stripe'), true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('verifySignature: stripe rejects a header missing t=', () => {
|
|
77
|
+
const secret = 'stripe-secret';
|
|
78
|
+
const body = '{"id":"evt_1"}';
|
|
79
|
+
const t = Math.floor(Date.now() / 1000);
|
|
80
|
+
const v1 = hmacHex(secret, `${t}.${body}`);
|
|
81
|
+
assert.equal(verifySignature(secret, body, `v1=${v1}`, 'stripe'), false);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('verifySignature: stripe rejects a header missing v1=', () => {
|
|
85
|
+
const secret = 'stripe-secret';
|
|
86
|
+
const body = '{"id":"evt_1"}';
|
|
87
|
+
const t = Math.floor(Date.now() / 1000);
|
|
88
|
+
assert.equal(verifySignature(secret, body, `t=${t}`, 'stripe'), false);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('verifySignature: stripe rejects a stale timestamp beyond the tolerance window (replay)', () => {
|
|
92
|
+
const secret = 'stripe-secret';
|
|
93
|
+
const body = '{"id":"evt_1"}';
|
|
94
|
+
const staleT = Math.floor((Date.now() - STRIPE_TOLERANCE_MS - 60_000) / 1000);
|
|
95
|
+
const v1 = hmacHex(secret, `${staleT}.${body}`);
|
|
96
|
+
assert.equal(verifySignature(secret, body, `t=${staleT},v1=${v1}`, 'stripe'), false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('verifySignature: stripe rejects a tampered v1 (HMAC mismatch) with a fresh t', () => {
|
|
100
|
+
const secret = 'stripe-secret';
|
|
101
|
+
const body = '{"id":"evt_1"}';
|
|
102
|
+
const t = Math.floor(Date.now() / 1000);
|
|
103
|
+
const tamperedV1 = hmacHex('wrong-secret', `${t}.${body}`);
|
|
104
|
+
assert.equal(verifySignature(secret, body, `t=${t},v1=${tamperedV1}`, 'stripe'), false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ── extractSignature: parser mapping + fallback scan ──────────────────
|
|
108
|
+
|
|
109
|
+
test('extractSignature: github parser strips the sha256= prefix from x-hub-signature-256', () => {
|
|
110
|
+
const headers = { 'x-hub-signature-256': 'sha256=deadbeef' };
|
|
111
|
+
assert.equal(extractSignature(headers, 'github'), 'deadbeef');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('extractSignature: generic parser strips the sha256= prefix from x-signature-256', () => {
|
|
115
|
+
const headers = { 'x-signature-256': 'sha256=cafebabe' };
|
|
116
|
+
assert.equal(extractSignature(headers, 'generic'), 'cafebabe');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('extractSignature: stripe parser returns the raw stripe-signature value (no prefix)', () => {
|
|
120
|
+
const headers = { 'stripe-signature': 't=1,v1=abc123' };
|
|
121
|
+
assert.equal(extractSignature(headers, 'stripe'), 't=1,v1=abc123');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('extractSignature: sentry parser returns the raw sentry-hook-signature value', () => {
|
|
125
|
+
const headers = { 'sentry-hook-signature': 'raw-sig-value' };
|
|
126
|
+
assert.equal(extractSignature(headers, 'sentry'), 'raw-sig-value');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('extractSignature: unknown parser name falls back to scanning all known headers', () => {
|
|
130
|
+
const headers = { 'x-signature-256': 'sha256=fallback-hit' };
|
|
131
|
+
assert.equal(extractSignature(headers, 'no-such-parser'), 'fallback-hit');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('extractSignature: no parser given scans known headers in declaration order', () => {
|
|
135
|
+
const headers = { 'x-hub-signature-256': 'sha256=github-hit' };
|
|
136
|
+
assert.equal(extractSignature(headers, undefined), 'github-hit');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('extractSignature: returns null when no known signature header is present', () => {
|
|
140
|
+
const headers = { 'x-some-other-header': 'value' };
|
|
141
|
+
assert.equal(extractSignature(headers, 'github'), null);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ── endpoint config loader: WEBHOOK.md frontmatter + secret side file ─
|
|
145
|
+
|
|
146
|
+
test('loadEndpointConfig: loads frontmatter + reads secret from side file', () => {
|
|
147
|
+
const name = 'my-endpoint';
|
|
148
|
+
const dir = join(dataDir, 'webhooks', name);
|
|
149
|
+
mkdirSync(dir, { recursive: true });
|
|
150
|
+
writeFileSync(
|
|
151
|
+
join(dir, 'WEBHOOK.md'),
|
|
152
|
+
[
|
|
153
|
+
'---',
|
|
154
|
+
'channel: main',
|
|
155
|
+
'parser: github',
|
|
156
|
+
'enabled: true',
|
|
157
|
+
'---',
|
|
158
|
+
'',
|
|
159
|
+
'Handle this webhook by summarizing the payload.',
|
|
160
|
+
'',
|
|
161
|
+
].join('\n'),
|
|
162
|
+
'utf8',
|
|
163
|
+
);
|
|
164
|
+
writeFileSync(join(dir, 'secret'), 'my-webhook-secret\n', 'utf8');
|
|
165
|
+
|
|
166
|
+
const cfg = loadEndpointConfig(name);
|
|
167
|
+
assert.equal(cfg.channel, 'main');
|
|
168
|
+
assert.equal(cfg.parser, 'github');
|
|
169
|
+
assert.equal(cfg.enabled, true);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('loadEndpointConfig: enabled: false frontmatter casts to boolean false', () => {
|
|
173
|
+
const name = 'disabled-endpoint';
|
|
174
|
+
const dir = join(dataDir, 'webhooks', name);
|
|
175
|
+
mkdirSync(dir, { recursive: true });
|
|
176
|
+
writeFileSync(
|
|
177
|
+
join(dir, 'WEBHOOK.md'),
|
|
178
|
+
['---', 'channel: main', 'enabled: false', '---', '', 'body text', ''].join('\n'),
|
|
179
|
+
'utf8',
|
|
180
|
+
);
|
|
181
|
+
const cfg = loadEndpointConfig(name);
|
|
182
|
+
assert.equal(cfg.enabled, false);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('loadEndpointConfig: returns null for a name with no WEBHOOK.md', () => {
|
|
186
|
+
assert.equal(loadEndpointConfig('never-registered'), null);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// loadEndpointConfig only surfaces the frontmatter (by design — see
|
|
190
|
+
// webhook.mjs comment above loadEndpointConfig); confirm the underlying
|
|
191
|
+
// shared parser it delegates to also recovers the markdown body, since
|
|
192
|
+
// the loader's frontmatter/body split is not independently exercised
|
|
193
|
+
// through the exported loadEndpointConfig surface.
|
|
194
|
+
test('readMarkdownDocument: WEBHOOK.md-shaped input yields frontmatter + body', () => {
|
|
195
|
+
const raw = [
|
|
196
|
+
'---',
|
|
197
|
+
'channel: main',
|
|
198
|
+
'parser: github',
|
|
199
|
+
'---',
|
|
200
|
+
'',
|
|
201
|
+
'Handle this webhook by summarizing the payload.',
|
|
202
|
+
'',
|
|
203
|
+
].join('\n');
|
|
204
|
+
const { frontmatter, body } = readMarkdownDocument(raw);
|
|
205
|
+
assert.equal(frontmatter.channel, 'main');
|
|
206
|
+
assert.equal(frontmatter.parser, 'github');
|
|
207
|
+
assert.equal(body, 'Handle this webhook by summarizing the payload.');
|
|
208
|
+
});
|
|
@@ -8,4 +8,7 @@ Root-cause analysis agent.
|
|
|
8
8
|
|
|
9
9
|
Smallest confirmed cause chain before fixes. Return likely cause, evidence
|
|
10
10
|
(`file:line`), smallest next check/fix. Mark confirmed facts vs inferences;
|
|
11
|
-
avoid broad speculation.
|
|
11
|
+
avoid broad speculation. Fragments only; no narration, no report bloat.
|
|
12
|
+
|
|
13
|
+
Converge, don't sweep: when new reads stop adding evidence, report the best
|
|
14
|
+
cause chain so far — a partial/blocked report is a valid completion.
|
|
@@ -11,10 +11,11 @@ growing blast radius, or Lead-only verification.
|
|
|
11
11
|
EDIT-FIRST DISCIPLINE. Survey the slice with ONE batched read/grep round,
|
|
12
12
|
then start patching the first bounded piece — broad scope means edit
|
|
13
13
|
incrementally, not read exhaustively. NEVER "one more confirming read": if
|
|
14
|
-
an anchor is plausible, the next call is `apply_patch`.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
is Lead's.
|
|
14
|
+
an anchor is plausible, the next call is `apply_patch`. Repeated read-only
|
|
15
|
+
turns without an edit = stalling; any runtime reminder is your cue to patch
|
|
16
|
+
the piece you understand or report blocked — a blocked report is a valid
|
|
17
|
+
completion. Self-check comes AFTER edits; deep verification is Lead's.
|
|
18
18
|
|
|
19
|
-
Minimal checks + how-to-verify.
|
|
19
|
+
Minimal checks + how-to-verify. Hand off outcome as fragments anchored to
|
|
20
|
+
`file:line`; no narration, no report bloat.
|
|
20
21
|
|
|
@@ -5,3 +5,7 @@ permission: read-write
|
|
|
5
5
|
# Maintainer
|
|
6
6
|
|
|
7
7
|
Maintenance and cleanup agent.
|
|
8
|
+
|
|
9
|
+
Smallest coherent change; no scope growth. Repeated read-only turns without
|
|
10
|
+
an edit = stalling; patch or return blocked — a blocked report is a valid
|
|
11
|
+
completion. Hand off as fragments (`file:line`); no narration.
|
|
@@ -8,4 +8,5 @@ Regression/risk review agent.
|
|
|
8
8
|
|
|
9
9
|
Find actionable correctness/regression/security/verification risks. Findings
|
|
10
10
|
first, severity-ordered, one line with `file:line`; skip non-risky nits. If
|
|
11
|
-
clean, one line + only material residual risk.
|
|
11
|
+
clean, one line + only material residual risk. Fragments only; no narration,
|
|
12
|
+
no report bloat.
|
|
@@ -10,8 +10,12 @@ Smallest scoped change; no drive-by cleanup. Stop when done/blocked.
|
|
|
10
10
|
EDIT-FIRST DISCIPLINE. Brief anchors (`file:line`) are pre-verified — trust
|
|
11
11
|
them and patch. No anchor: locate with AT MOST 1-2 reads, then edit. NEVER
|
|
12
12
|
"one more confirming read": if you know the file and the change, the next
|
|
13
|
-
call is `apply_patch`.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Lead/Reviewer
|
|
13
|
+
call is `apply_patch`. Repeated read-only turns without an edit = stalling;
|
|
14
|
+
any runtime reminder is your cue to patch now or return blocked with what's
|
|
15
|
+
missing — a blocked report is a valid completion. Confidence threshold is
|
|
16
|
+
"plausible", not "proven"; self-check comes AFTER the edit, and Lead/Reviewer
|
|
17
|
+
own final verification.
|
|
18
|
+
|
|
19
|
+
Hand off outcome as fragments anchored to `file:line`; no narration, no
|
|
20
|
+
report bloat.
|
|
17
21
|
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* Source files (rules/):
|
|
22
22
|
* - shared/01-tool.md — universal tool policy (Lead + agent BP1, identical full set)
|
|
23
23
|
* - lead/lead-tool.md — Lead-specific control-tower / delegation / ToolSearch guidance
|
|
24
|
+
* - lead/lead-brief.md — Lead brief contract (agent handoff briefs)
|
|
24
25
|
* - lead/01-02 — Lead general / channels
|
|
25
26
|
* - output-styles/<name>.md — Lead output style, selected by config outputStyle
|
|
26
27
|
* - agent/00-common.md — agent common behavior + universal worker contract (BP2)
|
|
@@ -275,6 +276,9 @@ function buildLeadRoleContent({ PLUGIN_ROOT, DATA_DIR }) {
|
|
|
275
276
|
const toolLead = readOptional(path.join(LEAD_DIR, 'lead-tool.md'));
|
|
276
277
|
if (toolLead) parts.push(toolLead);
|
|
277
278
|
|
|
279
|
+
const briefLead = readOptional(path.join(LEAD_DIR, 'lead-brief.md'));
|
|
280
|
+
if (briefLead) parts.push(briefLead);
|
|
281
|
+
|
|
278
282
|
if (generalSplit.role) parts.push(generalSplit.role);
|
|
279
283
|
|
|
280
284
|
const channels = readOptional(path.join(LEAD_DIR, '02-channels.md'));
|