profclaw 2.0.0 → 2.4.0
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/.env.example +14 -2
- package/CHANGELOG.md +52 -0
- package/CONTRIBUTING.md +20 -0
- package/Dockerfile +39 -23
- package/README.md +178 -41
- package/config/pricing.yml +26 -2
- package/dist/agents/agent-summary.d.ts +44 -0
- package/dist/agents/agent-summary.js +176 -0
- package/dist/agents/auto-memory.d.ts +59 -0
- package/dist/agents/auto-memory.js +310 -0
- package/dist/agents/checkpoint-manager.d.ts +76 -0
- package/dist/agents/checkpoint-manager.js +156 -0
- package/dist/agents/circuit-breaker.d.ts +54 -0
- package/dist/agents/circuit-breaker.js +153 -0
- package/dist/agents/command-sandbox.d.ts +93 -0
- package/dist/agents/command-sandbox.js +235 -0
- package/dist/agents/context-compactor.d.ts +70 -0
- package/dist/agents/context-compactor.js +361 -0
- package/dist/agents/conversation-branch.d.ts +55 -0
- package/dist/agents/conversation-branch.js +115 -0
- package/dist/agents/error-recovery.d.ts +44 -0
- package/dist/agents/error-recovery.js +197 -0
- package/dist/agents/events.d.ts +76 -0
- package/dist/agents/events.js +8 -0
- package/dist/agents/executor-runner.d.ts +135 -0
- package/dist/agents/executor-runner.js +303 -0
- package/dist/agents/executor.d.ts +37 -1
- package/dist/agents/executor.js +606 -124
- package/dist/agents/file-snapshots.d.ts +72 -0
- package/dist/agents/file-snapshots.js +182 -0
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/orchestrator.d.ts +183 -0
- package/dist/agents/orchestrator.js +495 -0
- package/dist/agents/output-truncator.d.ts +59 -0
- package/dist/agents/output-truncator.js +128 -0
- package/dist/agents/permissions.d.ts +63 -0
- package/dist/agents/permissions.js +138 -0
- package/dist/agents/plan-mode.d.ts +71 -0
- package/dist/agents/plan-mode.js +186 -0
- package/dist/agents/prompt-suggestions.d.ts +33 -0
- package/dist/agents/prompt-suggestions.js +208 -0
- package/dist/agents/rate-limit-monitor.d.ts +56 -0
- package/dist/agents/rate-limit-monitor.js +224 -0
- package/dist/agents/receipt/receipt-cli.d.ts +43 -0
- package/dist/agents/receipt/receipt-cli.js +82 -0
- package/dist/agents/receipt/receipt-types.d.ts +136 -0
- package/dist/agents/receipt/receipt-types.js +19 -0
- package/dist/agents/receipt/receipt-verify.d.ts +19 -0
- package/dist/agents/receipt/receipt-verify.js +176 -0
- package/dist/agents/receipt/receipt-view.d.ts +29 -0
- package/dist/agents/receipt/receipt-view.js +227 -0
- package/dist/agents/receipt/receipt-writer.d.ts +47 -0
- package/dist/agents/receipt/receipt-writer.js +186 -0
- package/dist/agents/result-store.d.ts +43 -0
- package/dist/agents/result-store.js +145 -0
- package/dist/agents/session-diff.d.ts +49 -0
- package/dist/agents/session-diff.js +249 -0
- package/dist/agents/tool-loader.d.ts +65 -0
- package/dist/agents/tool-loader.js +273 -0
- package/dist/agents/tool-selector.d.ts +123 -0
- package/dist/agents/tool-selector.js +321 -0
- package/dist/agents/transcript.d.ts +96 -0
- package/dist/agents/transcript.js +267 -0
- package/dist/agents/verified-run-git.d.ts +49 -0
- package/dist/agents/verified-run-git.js +82 -0
- package/dist/agents/verified-run.d.ts +149 -0
- package/dist/agents/verified-run.js +402 -0
- package/dist/agents/verifier.d.ts +64 -0
- package/dist/agents/verifier.js +186 -0
- package/dist/agents/worktree-manager.d.ts +80 -0
- package/dist/agents/worktree-manager.js +203 -0
- package/dist/agents/worktree-tools.d.ts +47 -0
- package/dist/agents/worktree-tools.js +246 -0
- package/dist/auth/middleware.js +14 -0
- package/dist/chat/agentic-executor.d.ts +7 -0
- package/dist/chat/agentic-executor.js +263 -32
- package/dist/chat/conversations.d.ts +2 -0
- package/dist/chat/conversations.js +8 -0
- package/dist/chat/execution/executor.d.ts +8 -0
- package/dist/chat/execution/executor.js +157 -103
- package/dist/chat/execution/index.js +5 -1
- package/dist/chat/execution/sandbox.d.ts +8 -0
- package/dist/chat/execution/sandbox.js +25 -1
- package/dist/chat/execution/tool-router.js +40 -14
- package/dist/chat/execution/tools/canvas.d.ts +2 -2
- package/dist/chat/execution/tools/code-tools.d.ts +66 -0
- package/dist/chat/execution/tools/code-tools.js +353 -0
- package/dist/chat/execution/tools/complete-task.d.ts +2 -2
- package/dist/chat/execution/tools/cron-tool.d.ts +26 -10
- package/dist/chat/execution/tools/cron-tool.js +48 -3
- package/dist/chat/execution/tools/decompose-task.d.ts +58 -0
- package/dist/chat/execution/tools/decompose-task.js +166 -0
- package/dist/chat/execution/tools/exec.d.ts +5 -2
- package/dist/chat/execution/tools/exec.js +294 -155
- package/dist/chat/execution/tools/feed-tools.d.ts +77 -0
- package/dist/chat/execution/tools/feed-tools.js +142 -0
- package/dist/chat/execution/tools/file-ops.d.ts +1 -1
- package/dist/chat/execution/tools/github.d.ts +2 -2
- package/dist/chat/execution/tools/index.d.ts +14 -2
- package/dist/chat/execution/tools/index.js +86 -12
- package/dist/chat/execution/tools/integrations.d.ts +8 -8
- package/dist/chat/execution/tools/multi-patch.d.ts +53 -0
- package/dist/chat/execution/tools/multi-patch.js +373 -0
- package/dist/chat/execution/tools/openai-image-gen.d.ts +2 -2
- package/dist/chat/execution/tools/plan-tools.d.ts +80 -0
- package/dist/chat/execution/tools/plan-tools.js +134 -0
- package/dist/chat/execution/tools/profclaw-ops.d.ts +6 -6
- package/dist/chat/execution/tools/repl-tool.d.ts +46 -0
- package/dist/chat/execution/tools/repl-tool.js +207 -0
- package/dist/chat/execution/tools/sessions-send.d.ts +2 -2
- package/dist/chat/execution/tools/slack-actions.d.ts +2 -2
- package/dist/chat/execution/tools/system.d.ts +4 -4
- package/dist/chat/execution/tools/todo-tool.d.ts +72 -0
- package/dist/chat/execution/tools/todo-tool.js +152 -0
- package/dist/chat/execution/tools/tool-search.d.ts +50 -0
- package/dist/chat/execution/tools/tool-search.js +155 -0
- package/dist/chat/execution/tools/tts-speak.d.ts +2 -2
- package/dist/chat/execution/types.d.ts +21 -14
- package/dist/chat/execution/workflows/executor.d.ts +11 -0
- package/dist/chat/execution/workflows/executor.js +99 -1
- package/dist/chat/execution/workflows/types.d.ts +35 -2
- package/dist/chat/format/index.d.ts +1 -0
- package/dist/chat/format/index.js +1 -0
- package/dist/chat/format/markdown-convert.d.ts +23 -0
- package/dist/chat/format/markdown-convert.js +149 -0
- package/dist/chat/index.d.ts +1 -1
- package/dist/chat/index.js +1 -1
- package/dist/chat/message-handler.js +58 -6
- package/dist/chat/meta-tool-handlers.d.ts +18 -0
- package/dist/chat/meta-tool-handlers.js +51 -0
- package/dist/chat/proactive/index.d.ts +62 -0
- package/dist/chat/proactive/index.js +147 -0
- package/dist/chat/prompt-adapter.js +2 -0
- package/dist/chat/providers/irc/index.d.ts +2 -2
- package/dist/chat/providers/telegram/index.d.ts +2 -0
- package/dist/chat/providers/telegram/index.js +21 -2
- package/dist/chat/system-prompts.d.ts +3 -3
- package/dist/chat/system-prompts.js +121 -61
- package/dist/chat/tool-handler.d.ts +2 -0
- package/dist/chat/tool-handler.js +47 -3
- package/dist/chat/tools.d.ts +6 -6
- package/dist/cli/commands/auth.js +64 -1
- package/dist/cli/commands/chat.js +1327 -138
- package/dist/cli/commands/doctor.js +222 -16
- package/dist/cli/commands/history.d.ts +3 -0
- package/dist/cli/commands/history.js +138 -0
- package/dist/cli/commands/import.d.ts +44 -0
- package/dist/cli/commands/import.js +526 -0
- package/dist/cli/commands/init.d.ts +13 -0
- package/dist/cli/commands/init.js +381 -0
- package/dist/cli/commands/onboard.js +210 -30
- package/dist/cli/commands/plan.d.ts +13 -0
- package/dist/cli/commands/plan.js +184 -0
- package/dist/cli/commands/receipt.d.ts +7 -0
- package/dist/cli/commands/receipt.js +90 -0
- package/dist/cli/commands/run.d.ts +28 -0
- package/dist/cli/commands/run.js +204 -0
- package/dist/cli/commands/serve.d.ts +7 -0
- package/dist/cli/commands/serve.js +77 -18
- package/dist/cli/commands/setup.js +84 -3
- package/dist/cli/commands/status.js +54 -0
- package/dist/cli/commands/tools.js +13 -13
- package/dist/cli/commands/tui.js +26 -1
- package/dist/cli/index.js +23 -0
- package/dist/cli/ink/App.d.ts +13 -0
- package/dist/cli/ink/App.js +6 -0
- package/dist/cli/ink/ChatApp.d.ts +60 -0
- package/dist/cli/ink/ChatApp.js +325 -0
- package/dist/cli/ink/DashboardApp.d.ts +3 -0
- package/dist/cli/ink/DashboardApp.js +143 -0
- package/dist/cli/ink/components/AgentStatus.d.ts +17 -0
- package/dist/cli/ink/components/AgentStatus.js +33 -0
- package/dist/cli/ink/components/ChannelList.d.ts +17 -0
- package/dist/cli/ink/components/ChannelList.js +33 -0
- package/dist/cli/ink/components/ConnectionStatus.d.ts +19 -0
- package/dist/cli/ink/components/ConnectionStatus.js +50 -0
- package/dist/cli/ink/components/CostBar.d.ts +19 -0
- package/dist/cli/ink/components/CostBar.js +35 -0
- package/dist/cli/ink/components/HookStatus.d.ts +15 -0
- package/dist/cli/ink/components/HookStatus.js +7 -0
- package/dist/cli/ink/components/PermissionPrompt.d.ts +21 -0
- package/dist/cli/ink/components/PermissionPrompt.js +51 -0
- package/dist/cli/ink/components/PlanView.d.ts +23 -0
- package/dist/cli/ink/components/PlanView.js +26 -0
- package/dist/cli/ink/components/ProviderList.d.ts +16 -0
- package/dist/cli/ink/components/ProviderList.js +16 -0
- package/dist/cli/ink/components/SessionHeader.d.ts +15 -0
- package/dist/cli/ink/components/SessionHeader.js +15 -0
- package/dist/cli/ink/components/StreamingMessage.d.ts +20 -0
- package/dist/cli/ink/components/StreamingMessage.js +27 -0
- package/dist/cli/ink/components/SuggestionBar.d.ts +15 -0
- package/dist/cli/ink/components/SuggestionBar.js +28 -0
- package/dist/cli/ink/components/SystemHealth.d.ts +20 -0
- package/dist/cli/ink/components/SystemHealth.js +39 -0
- package/dist/cli/ink/components/TaskList.d.ts +17 -0
- package/dist/cli/ink/components/TaskList.js +59 -0
- package/dist/cli/ink/components/ToolCall.d.ts +17 -0
- package/dist/cli/ink/components/ToolCall.js +43 -0
- package/dist/cli/ink/index.d.ts +23 -0
- package/dist/cli/ink/index.js +15 -0
- package/dist/cli/ink/keybindings.d.ts +35 -0
- package/dist/cli/ink/keybindings.js +105 -0
- package/dist/cli/ink/markdown-renderer.d.ts +47 -0
- package/dist/cli/ink/markdown-renderer.js +380 -0
- package/dist/cli/ink/output-styles.d.ts +39 -0
- package/dist/cli/ink/output-styles.js +119 -0
- package/dist/cli/interactive/auto-serve.d.ts +38 -0
- package/dist/cli/interactive/auto-serve.js +209 -0
- package/dist/cli/interactive/index.d.ts +25 -0
- package/dist/cli/interactive/index.js +23 -0
- package/dist/cli/interactive/picker.d.ts +28 -0
- package/dist/cli/interactive/picker.js +153 -0
- package/dist/cli/interactive/renderer.d.ts +26 -0
- package/dist/cli/interactive/renderer.js +555 -0
- package/dist/cli/interactive/repl.d.ts +14 -0
- package/dist/cli/interactive/repl.js +1516 -0
- package/dist/cli/interactive/stream-client.d.ts +59 -0
- package/dist/cli/interactive/stream-client.js +506 -0
- package/dist/cli/interactive/types.d.ts +121 -0
- package/dist/cli/interactive/types.js +10 -0
- package/dist/cli/utils/api.d.ts +9 -0
- package/dist/cli/utils/api.js +58 -2
- package/dist/costs/token-tracker.d.ts +10 -1
- package/dist/costs/token-tracker.js +37 -5
- package/dist/cron/natural-language.d.ts +54 -0
- package/dist/cron/natural-language.js +569 -0
- package/dist/cron/scheduler.d.ts +34 -4
- package/dist/cron/scheduler.js +141 -9
- package/dist/cron/templates.d.ts +1 -1
- package/dist/cron/templates.js +219 -0
- package/dist/feeds/feed-engine.d.ts +149 -0
- package/dist/feeds/feed-engine.js +600 -0
- package/dist/feeds/index.d.ts +9 -0
- package/dist/feeds/index.js +8 -0
- package/dist/gateway/router.js +6 -1
- package/dist/hooks/built-in/audit-log.d.ts +11 -0
- package/dist/hooks/built-in/audit-log.js +84 -0
- package/dist/hooks/built-in/cost-warning.d.ts +10 -0
- package/dist/hooks/built-in/cost-warning.js +59 -0
- package/dist/hooks/built-in/dangerous-tool.d.ts +32 -0
- package/dist/hooks/built-in/dangerous-tool.js +80 -0
- package/dist/hooks/built-in/index.d.ts +14 -0
- package/dist/hooks/built-in/index.js +20 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/loader.d.ts +17 -0
- package/dist/hooks/loader.js +142 -0
- package/dist/hooks/registry.d.ts +62 -0
- package/dist/hooks/registry.js +127 -0
- package/dist/integrations/web-search.js +6 -3
- package/dist/memory/index.d.ts +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/memory-service.js +14 -13
- package/dist/memory/observational.d.ts +62 -0
- package/dist/memory/observational.js +396 -0
- package/dist/middleware/content-filter.d.ts +22 -0
- package/dist/middleware/content-filter.js +69 -0
- package/dist/middleware/request-validator.d.ts +159 -0
- package/dist/middleware/request-validator.js +93 -0
- package/dist/plugins/sdk.d.ts +22 -0
- package/dist/plugins/sdk.js +17 -1
- package/dist/projects/types.d.ts +10 -10
- package/dist/providers/ai-sdk.d.ts +11 -0
- package/dist/providers/ai-sdk.js +111 -56
- package/dist/providers/core/models.js +114 -6
- package/dist/providers/core/types.d.ts +7 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/prompt-cache.d.ts +68 -0
- package/dist/providers/prompt-cache.js +165 -0
- package/dist/providers/smart-router.d.ts +118 -0
- package/dist/providers/smart-router.js +693 -0
- package/dist/providers/types.d.ts +16 -16
- package/dist/routes/agents.js +50 -0
- package/dist/routes/auth.js +8 -1
- package/dist/routes/chat.js +280 -18
- package/dist/routes/costs.js +32 -0
- package/dist/routes/cron.js +80 -5
- package/dist/routes/feeds.d.ts +10 -0
- package/dist/routes/feeds.js +168 -0
- package/dist/routes/mcp.js +21 -0
- package/dist/routes/memory.js +130 -0
- package/dist/routes/skills.js +129 -0
- package/dist/routes/teams.d.ts +9 -0
- package/dist/routes/teams.js +210 -0
- package/dist/routes/tools.js +14 -0
- package/dist/routes/users.js +2 -0
- package/dist/server/route-loader.js +3 -1
- package/dist/server/stream-bridge.d.ts +19 -0
- package/dist/server/stream-bridge.js +38 -0
- package/dist/server.js +313 -36
- package/dist/skills/marketplace.d.ts +87 -0
- package/dist/skills/marketplace.js +268 -0
- package/dist/storage/index.d.ts +14 -0
- package/dist/storage/index.js +64 -3
- package/dist/storage/schema.d.ts +4 -4
- package/dist/storage/schema.js +1 -1
- package/dist/teams/index.d.ts +110 -0
- package/dist/teams/index.js +365 -0
- package/dist/tickets/types.d.ts +39 -39
- package/dist/types/summary.d.ts +5 -5
- package/dist/utils/auto-update.d.ts +23 -0
- package/dist/utils/auto-update.js +157 -0
- package/dist/utils/offline-detect.d.ts +55 -0
- package/dist/utils/offline-detect.js +121 -0
- package/dist/utils/pid-file.d.ts +36 -0
- package/dist/utils/pid-file.js +125 -0
- package/dist/utils/prevent-sleep.d.ts +27 -0
- package/dist/utils/prevent-sleep.js +95 -0
- package/dist/utils/sanitizer.d.ts +57 -0
- package/dist/utils/sanitizer.js +174 -0
- package/docker-compose.yml +13 -137
- package/package.json +31 -6
- package/skills/deploy-cloudflare/SKILL.md +62 -0
- package/skills/deploy-vercel/SKILL.md +67 -0
- package/skills/docker-deploy/SKILL.md +158 -0
- package/skills/full-stack-builder/SKILL.md +92 -0
- package/skills/humanizer/SKILL.md +488 -0
- package/skills/profclaw-assistant/SKILL.md +85 -56
- package/ui/dist/assets/ActivityView-Da6HQ8ws.js +1 -0
- package/ui/dist/assets/AgentList-H58R0kX3.js +1 -0
- package/ui/dist/assets/AnalyticsDashboard-B28AeRsc.js +1 -0
- package/ui/dist/assets/AreaChart-DwLzeAQ8.js +1 -0
- package/ui/dist/assets/CartesianChart-piKQsTAR.js +36 -0
- package/ui/dist/assets/ChatView-Dbj8rxU5.js +10 -0
- package/ui/dist/assets/CostsDashboard-C6F5df0F.js +1 -0
- package/ui/dist/assets/EstimateSelect-wga0rUKX.js +1 -0
- package/ui/dist/assets/FeedDashboard-Ef6FEzEf.js +1 -0
- package/ui/dist/assets/FloatingChatbot-0yhkUeCq.js +2 -0
- package/ui/dist/assets/InviteCodeManagement-tUXfx4Bc.js +2 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/ui/dist/assets/MarketplacePage-DGrLtolR.js +1 -0
- package/ui/dist/assets/{ProjectDetail--gPfvATB.js → ProjectDetail-BgWvA6UP.js} +2 -2
- package/ui/dist/assets/ProjectIcon-TDuliqwg.js +1 -0
- package/ui/dist/assets/ProjectList-DXgM4187.js +1 -0
- package/ui/dist/assets/{Settings-1kaf7VHE.js → Settings-xA8Zhi3p.js} +3 -3
- package/ui/dist/assets/StatusIndicator-B8vfFAxg.js +1 -0
- package/ui/dist/assets/{SummaryDetail-DV3AmONk.js → SummaryDetail-eedRfTtF.js} +2 -2
- package/ui/dist/assets/SummaryList-CLNX2P84.js +1 -0
- package/ui/dist/assets/TaskDetail-BtWclNiR.js +1 -0
- package/ui/dist/assets/TaskList-JjODAgSc.js +1 -0
- package/ui/dist/assets/TeamsPage-CBK5iAyx.js +1 -0
- package/ui/dist/assets/TicketBoard-C9aJNNYp.js +1 -0
- package/ui/dist/assets/TicketDetail-CV5JmL31.js +1 -0
- package/ui/dist/assets/TicketList-68V4lzUz.js +1 -0
- package/ui/dist/assets/UserManagement-DKTb6UpE.js +2 -0
- package/ui/dist/assets/ViewSwitcher-BvdQht2Y.js +1 -0
- package/ui/dist/assets/alert-dialog-O6qBOv_f.js +1 -0
- package/ui/dist/assets/{archive-BsLa7brF.js → archive-CX7UvZpf.js} +1 -1
- package/ui/dist/assets/badge-BbfnwDJP.js +1 -0
- package/ui/dist/assets/{bug-CmZI7jqM.js → bug-3WX0394-.js} +1 -1
- package/ui/dist/assets/{calendar-Bf0t2B82.js → calendar-buq77TaS.js} +1 -1
- package/ui/dist/assets/{chevron-up-CoUsOvZj.js → chevron-up-4ymsBJRo.js} +1 -1
- package/ui/dist/assets/{circle-x-Dgw11LgT.js → circle-x-ByXtwR6Y.js} +1 -1
- package/ui/dist/assets/constants-CVdDh7p0.js +1 -0
- package/ui/dist/assets/{cpu-DiPMQICV.js → cpu-CiuoCeDY.js} +1 -1
- package/ui/dist/assets/{database-BwTfupDi.js → database-BWXSBg5a.js} +1 -1
- package/ui/dist/assets/dialog-lDg4NvMV.js +1 -0
- package/ui/dist/assets/dollar-sign-Dmijd6DP.js +1 -0
- package/ui/dist/assets/{ellipsis-UJx4Xnf6.js → ellipsis-vT-5SVIp.js} +1 -1
- package/ui/dist/assets/{ellipsis-vertical-DoULYEKn.js → ellipsis-vertical-B4tAX_bL.js} +1 -1
- package/ui/dist/assets/feature-markdown-DBo5Q7uq.js +289 -0
- package/ui/dist/assets/{file-code-B2UrJLwi.js → file-code-ChOWdKXf.js} +1 -1
- package/ui/dist/assets/{folder-open-BJi577JH.js → folder-open-CkOUbIiP.js} +1 -1
- package/ui/dist/assets/{funnel-PkaD_HSR.js → funnel-uZkoSPP-.js} +1 -1
- package/ui/dist/assets/{git-branch-C_K30am0.js → git-branch-CwvUxj7C.js} +1 -1
- package/ui/dist/assets/{git-commit-horizontal-IT6ecxWQ.js → git-commit-horizontal-lxPzCvhD.js} +1 -1
- package/ui/dist/assets/{globe-f-jjCa_g.js → globe-D_WpJdsj.js} +1 -1
- package/ui/dist/assets/{hash-DNEv8Elo.js → hash-CA0hJ7vh.js} +1 -1
- package/ui/dist/assets/{history-BQSBo_1Q.js → history-CEWMLELs.js} +1 -1
- package/ui/dist/assets/inbox-ehtOptTV.js +1 -0
- package/ui/dist/assets/{index-nS3EErDg.js → index-C1rhbml4.js} +3 -3
- package/ui/dist/assets/index-CR8gfCHv.js +1 -0
- package/ui/dist/assets/index-DVXMpWai.js +1 -0
- package/ui/dist/assets/index-DaDJMM2v.js +1 -0
- package/ui/dist/assets/index-Djdq_0xu.js +1 -0
- package/ui/dist/assets/index-M46Gcb8K.css +1 -0
- package/ui/dist/assets/index-sEBN7w8J.js +109 -0
- package/ui/dist/assets/layers-C6Mhf8Nk.js +1 -0
- package/ui/dist/assets/{layout-dashboard-CcKt1hXw.js → layout-dashboard-BRUM0kpT.js} +1 -1
- package/ui/dist/assets/{lightbulb-Jhp7FvbU.js → lightbulb-CN2q1O5l.js} +1 -1
- package/ui/dist/assets/{link-2-DdO7ostF.js → link-2-CC4E28yJ.js} +1 -1
- package/ui/dist/assets/markdown-renderer-C6ENbB0u.js +10 -0
- package/ui/dist/assets/markdown-renderer-CfVKi3_s.css +1 -0
- package/ui/dist/assets/{message-square-CBgH65X7.js → message-square-Bbb241am.js} +1 -1
- package/ui/dist/assets/package-CwT3IzFx.js +1 -0
- package/ui/dist/assets/{pencil-CYXCEPQh.js → pencil-CDmiHzoj.js} +1 -1
- package/ui/dist/assets/{play-d1ONaH0-.js → play-tjy5jMQX.js} +1 -1
- package/ui/dist/assets/popover-CFHZf3pE.js +1 -0
- package/ui/dist/assets/progress-BboUHuLf.js +1 -0
- package/ui/dist/assets/{rocket-DhvUetsk.js → rocket--8ZHgdXJ.js} +1 -1
- package/ui/dist/assets/select-n108rXny.js +1 -0
- package/ui/dist/assets/{send-P0r5D7td.js → send-Cu7MR2Qh.js} +1 -1
- package/ui/dist/assets/server-DmodxZp-.js +1 -0
- package/ui/dist/assets/{settings-2-ocj5uHGx.js → settings-2-CkRNqkxj.js} +1 -1
- package/ui/dist/assets/sheet-7aUJkyzU.js +1 -0
- package/ui/dist/assets/{shield-check-DfVdE34l.js → shield-check-ViI3nMKE.js} +1 -1
- package/ui/dist/assets/{shield-off-8l0J6R-8.js → shield-off-DN3tagkn.js} +1 -1
- package/ui/dist/assets/skeleton-C0E1Nfro.js +1 -0
- package/ui/dist/assets/{smartphone-77_g3SQn.js → smartphone-5FghiXpA.js} +1 -1
- package/ui/dist/assets/{square-Ckzsr3Rn.js → square-DiNSx8K7.js} +1 -1
- package/ui/dist/assets/star-DhdFVidS.js +1 -0
- package/ui/dist/assets/switch-CIgVbZqQ.js +1 -0
- package/ui/dist/assets/table-BC56FVMV.js +1 -0
- package/ui/dist/assets/{tag-C35mgoEv.js → tag-CH_vQliw.js} +1 -1
- package/ui/dist/assets/textarea-CxDiKFXZ.js +1 -0
- package/ui/dist/assets/{timer-DHJBS2w1.js → timer-CdGVTb60.js} +1 -1
- package/ui/dist/assets/tooltip-BuW9A5UO.js +1 -0
- package/ui/dist/assets/{trash-2-keO5tKPE.js → trash-2-CucZJL0N.js} +1 -1
- package/ui/dist/assets/trending-down-BCezC1z2.js +1 -0
- package/ui/dist/assets/{trending-up-BBv8KbrY.js → trending-up-C5OYX2k2.js} +1 -1
- package/ui/dist/assets/vendor-query-C0p7b53x.js +4 -0
- package/ui/dist/assets/vendor-react-Mc5_kfYG.js +12 -0
- package/ui/dist/assets/vendor-ui-CXPAUBFv.js +51 -0
- package/ui/dist/assets/{wand-sparkles-BusQ52ZK.js → wand-sparkles-BaH6pJnc.js} +1 -1
- package/ui/dist/assets/{wrench-02YaOSiK.js → wrench-CjxjP6ok.js} +1 -1
- package/ui/dist/index.html +5 -2
- package/ui/dist/assets/ActivityView-BeshZXmE.js +0 -1
- package/ui/dist/assets/AgentList-QVHeldPi.js +0 -1
- package/ui/dist/assets/AnalyticsDashboard-C9R97dXE.js +0 -1
- package/ui/dist/assets/AreaChart-B73gaiVQ.js +0 -1
- package/ui/dist/assets/CartesianChart-CXYL2R-A.js +0 -36
- package/ui/dist/assets/ChatView-qjZErlrp.js +0 -10
- package/ui/dist/assets/CostsDashboard-O4DCLy1i.js +0 -1
- package/ui/dist/assets/EstimateSelect-1hQf4Osk.js +0 -1
- package/ui/dist/assets/FloatingChatbot-BZBzrJxd.js +0 -2
- package/ui/dist/assets/InviteCodeManagement-LEoGqYeA.js +0 -2
- package/ui/dist/assets/ProjectIcon-Chd_ofsw.js +0 -1
- package/ui/dist/assets/ProjectList-BvUfVB6c.js +0 -4
- package/ui/dist/assets/StatusIndicator-pRfkNcdV.js +0 -1
- package/ui/dist/assets/SummaryList-B6kXiTxL.js +0 -1
- package/ui/dist/assets/TaskDetail-CV6CzbOS.js +0 -1
- package/ui/dist/assets/TaskList-DL7brQKd.js +0 -1
- package/ui/dist/assets/TicketBoard-CrkkFj7C.js +0 -1
- package/ui/dist/assets/TicketDetail-hnnYclpD.js +0 -1
- package/ui/dist/assets/TicketList-CsDAyQCL.js +0 -1
- package/ui/dist/assets/UserManagement-BGFQyTSr.js +0 -2
- package/ui/dist/assets/ViewSwitcher-CnL0OqA8.js +0 -1
- package/ui/dist/assets/alert-dialog-SKM6Gjz4.js +0 -7
- package/ui/dist/assets/badge-CVHH1a-v.js +0 -1
- package/ui/dist/assets/brain-6o_RHVpe.js +0 -1
- package/ui/dist/assets/constants-Q5Zivl-a.js +0 -1
- package/ui/dist/assets/dialog-BUqN9Psj.js +0 -1
- package/ui/dist/assets/index-BUmnwPQr.js +0 -1
- package/ui/dist/assets/index-BfF5G4Cw.js +0 -1
- package/ui/dist/assets/index-C8N375c2.js +0 -1
- package/ui/dist/assets/index-CuxnqVVz.css +0 -1
- package/ui/dist/assets/index-CzMM3QOk.js +0 -1
- package/ui/dist/assets/index-lJlNPFVJ.js +0 -67
- package/ui/dist/assets/layers-jFqVrWMD.js +0 -1
- package/ui/dist/assets/markdown-renderer-n1ToC4XQ.js +0 -36
- package/ui/dist/assets/popover-CrmZgoWu.js +0 -1
- package/ui/dist/assets/progress-uv4SPkL5.js +0 -1
- package/ui/dist/assets/select-CeQ8AHGy.js +0 -1
- package/ui/dist/assets/server-CVHTZB72.js +0 -1
- package/ui/dist/assets/sheet-DuQExSho.js +0 -1
- package/ui/dist/assets/skeleton-TFiFJ3nB.js +0 -1
- package/ui/dist/assets/switch-CAePkd6k.js +0 -1
- package/ui/dist/assets/table-ZjAZ7hkt.js +0 -1
- package/ui/dist/assets/textarea-DsVPnSgR.js +0 -1
- package/ui/dist/assets/tooltip-BYmQ331W.js +0 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receipt writer: append-only JSONL, canonical JSON, SHA-256 hash chaining,
|
|
3
|
+
* secret redaction before hashing, optional Ed25519 signature on run_end.
|
|
4
|
+
* Spec: docs/specs/run-receipt-v0.md
|
|
5
|
+
*/
|
|
6
|
+
import { createHash, createPrivateKey, createPublicKey, sign as edSign } from 'node:crypto';
|
|
7
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import { GENESIS_HASH, RECEIPT_VERSION } from './receipt-types.js';
|
|
10
|
+
// Canonical JSON
|
|
11
|
+
/** Sorted keys, no whitespace, `undefined` members dropped, non-finite numbers rejected. */
|
|
12
|
+
export function canonicalJson(value) {
|
|
13
|
+
if (value === null)
|
|
14
|
+
return 'null';
|
|
15
|
+
switch (typeof value) {
|
|
16
|
+
case 'string':
|
|
17
|
+
case 'boolean':
|
|
18
|
+
return JSON.stringify(value);
|
|
19
|
+
case 'number':
|
|
20
|
+
if (!Number.isFinite(value))
|
|
21
|
+
throw new Error('canonicalJson: non-finite number');
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
case 'object': {
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
return `[${value.map((v) => (v === undefined ? 'null' : canonicalJson(v))).join(',')}]`;
|
|
26
|
+
}
|
|
27
|
+
const obj = value;
|
|
28
|
+
const parts = [];
|
|
29
|
+
for (const key of Object.keys(obj).sort()) {
|
|
30
|
+
if (obj[key] === undefined)
|
|
31
|
+
continue;
|
|
32
|
+
parts.push(`${JSON.stringify(key)}:${canonicalJson(obj[key])}`);
|
|
33
|
+
}
|
|
34
|
+
return `{${parts.join(',')}}`;
|
|
35
|
+
}
|
|
36
|
+
default:
|
|
37
|
+
throw new Error(`canonicalJson: unsupported type ${typeof value}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function sha256Hex(input) {
|
|
41
|
+
return createHash('sha256').update(input).digest('hex');
|
|
42
|
+
}
|
|
43
|
+
/** Hash of an event: canonical JSON without `hash` and `signature`. */
|
|
44
|
+
export function computeEventHash(event) {
|
|
45
|
+
const rest = { ...event };
|
|
46
|
+
delete rest['hash'];
|
|
47
|
+
delete rest['signature'];
|
|
48
|
+
return sha256Hex(canonicalJson(rest));
|
|
49
|
+
}
|
|
50
|
+
// Redaction
|
|
51
|
+
export const REDACTED = '[REDACTED]';
|
|
52
|
+
const SECRET_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL)/i;
|
|
53
|
+
const TOKEN_PATTERNS = [
|
|
54
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
55
|
+
/\bsk-[A-Za-z0-9_-]{16,}/g,
|
|
56
|
+
/\bgh[pousr]_[A-Za-z0-9]{20,}/g,
|
|
57
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/g,
|
|
58
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/g,
|
|
59
|
+
/\b(?:AKIA|ASIA)[0-9A-Z]{16}\b/g,
|
|
60
|
+
/\bAIza[0-9A-Za-z_-]{30,}/g,
|
|
61
|
+
/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
62
|
+
/\bnpm_[A-Za-z0-9]{30,}/g,
|
|
63
|
+
];
|
|
64
|
+
const BEARER = /\b(Bearer\s+)[A-Za-z0-9._~+/=-]{12,}/gi;
|
|
65
|
+
/** NAME=value or NAME: value, where NAME looks like a secret variable. */
|
|
66
|
+
const ENV_ASSIGN = /\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIALS?)[A-Z0-9_]*)(\s*[=:]\s*)("[^"\n]*"|'[^'\n]*'|[^\s"',;]+)/g;
|
|
67
|
+
export function redactString(input) {
|
|
68
|
+
let out = input;
|
|
69
|
+
for (const re of TOKEN_PATTERNS)
|
|
70
|
+
out = out.replace(re, REDACTED);
|
|
71
|
+
out = out.replace(BEARER, `$1${REDACTED}`);
|
|
72
|
+
out = out.replace(ENV_ASSIGN, (match, name, sep, value) => value === REDACTED || value === `"${REDACTED}"` ? match : `${name}${sep}${REDACTED}`);
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/** Deep-redacts strings and values under secret-looking object keys. Returns the changed flag. */
|
|
76
|
+
export function redactValue(value) {
|
|
77
|
+
if (typeof value === 'string') {
|
|
78
|
+
const next = redactString(value);
|
|
79
|
+
return { value: next, changed: next !== value };
|
|
80
|
+
}
|
|
81
|
+
if (Array.isArray(value)) {
|
|
82
|
+
let changed = false;
|
|
83
|
+
const out = value.map((item) => {
|
|
84
|
+
const r = redactValue(item);
|
|
85
|
+
changed = changed || r.changed;
|
|
86
|
+
return r.value;
|
|
87
|
+
});
|
|
88
|
+
return { value: out, changed };
|
|
89
|
+
}
|
|
90
|
+
if (value !== null && typeof value === 'object') {
|
|
91
|
+
let changed = false;
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const [key, v] of Object.entries(value)) {
|
|
94
|
+
if (SECRET_NAME.test(key) && typeof v === 'string' && v !== '' && v !== REDACTED) {
|
|
95
|
+
out[key] = REDACTED;
|
|
96
|
+
changed = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const r = redactValue(v);
|
|
100
|
+
changed = changed || r.changed;
|
|
101
|
+
out[key] = r.value;
|
|
102
|
+
}
|
|
103
|
+
return { value: out, changed };
|
|
104
|
+
}
|
|
105
|
+
return { value, changed: false };
|
|
106
|
+
}
|
|
107
|
+
/** Signer from a PKCS8 PEM Ed25519 private key. */
|
|
108
|
+
export function createSigner(privateKeyPem) {
|
|
109
|
+
const privateKey = createPrivateKey(privateKeyPem);
|
|
110
|
+
if (privateKey.asymmetricKeyType !== 'ed25519')
|
|
111
|
+
throw new Error('Receipt signing key must be Ed25519');
|
|
112
|
+
const publicKey = createPublicKey(privateKey).export({ type: 'spki', format: 'der' }).toString('base64');
|
|
113
|
+
return {
|
|
114
|
+
publicKey,
|
|
115
|
+
sign: (hash) => edSign(null, Buffer.from(hash, 'utf-8'), privateKey).toString('base64'),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function loadSignerFromFile(path) {
|
|
119
|
+
return createSigner(readFileSync(path, 'utf-8'));
|
|
120
|
+
}
|
|
121
|
+
/** Default location: <projectRoot>/.profclaw/runs/<runId>/receipt.jsonl (env PROFCLAW_RECEIPT_PATH overrides). */
|
|
122
|
+
export function defaultReceiptPath(projectRoot, runId, env = process.env) {
|
|
123
|
+
const override = env['PROFCLAW_RECEIPT_PATH']?.trim();
|
|
124
|
+
return override ? override : join(projectRoot, '.profclaw', 'runs', runId, 'receipt.jsonl');
|
|
125
|
+
}
|
|
126
|
+
export class ReceiptWriter {
|
|
127
|
+
path;
|
|
128
|
+
seq = 0;
|
|
129
|
+
prev = GENESIS_HASH;
|
|
130
|
+
ended = false;
|
|
131
|
+
signer;
|
|
132
|
+
redact;
|
|
133
|
+
now;
|
|
134
|
+
constructor(options) {
|
|
135
|
+
this.path = options.path;
|
|
136
|
+
this.signer = options.signer;
|
|
137
|
+
this.redact = options.redact ?? true;
|
|
138
|
+
this.now = options.now ?? (() => new Date());
|
|
139
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
140
|
+
// Append-only and one receipt per file: refuse to extend an existing file.
|
|
141
|
+
if (existsSync(this.path))
|
|
142
|
+
throw new Error(`Receipt already exists: ${this.path}`);
|
|
143
|
+
writeFileSync(this.path, '', { flag: 'wx' });
|
|
144
|
+
}
|
|
145
|
+
get lastHash() {
|
|
146
|
+
return this.prev;
|
|
147
|
+
}
|
|
148
|
+
append(type, data) {
|
|
149
|
+
if (this.ended)
|
|
150
|
+
throw new Error('Receipt already ended (run_end written)');
|
|
151
|
+
let payload = data;
|
|
152
|
+
if (this.redact) {
|
|
153
|
+
const r = redactValue(data);
|
|
154
|
+
payload = r.value;
|
|
155
|
+
if (type === 'tool_call' && r.changed) {
|
|
156
|
+
payload['redacted'] = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const base = {
|
|
160
|
+
v: RECEIPT_VERSION,
|
|
161
|
+
seq: this.seq,
|
|
162
|
+
ts: this.now().toISOString(),
|
|
163
|
+
type,
|
|
164
|
+
data: payload,
|
|
165
|
+
prev: this.prev,
|
|
166
|
+
};
|
|
167
|
+
const hash = computeEventHash(base);
|
|
168
|
+
const event = { ...base, hash };
|
|
169
|
+
if (type === 'run_end' && this.signer) {
|
|
170
|
+
const signature = {
|
|
171
|
+
alg: 'ed25519',
|
|
172
|
+
publicKey: this.signer.publicKey,
|
|
173
|
+
sig: this.signer.sign(hash),
|
|
174
|
+
};
|
|
175
|
+
event['signature'] = signature;
|
|
176
|
+
}
|
|
177
|
+
// Canonical text per line; hash and signature are members like any other.
|
|
178
|
+
appendFileSync(this.path, `${canonicalJson(event)}\n`, 'utf-8');
|
|
179
|
+
this.seq++;
|
|
180
|
+
this.prev = hash;
|
|
181
|
+
if (type === 'run_end')
|
|
182
|
+
this.ended = true;
|
|
183
|
+
return event;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=receipt-writer.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Store
|
|
3
|
+
*
|
|
4
|
+
* Manages large tool results by spilling them to disk when they exceed the
|
|
5
|
+
* inline size limit. This prevents context windows from being flooded by
|
|
6
|
+
* large tool outputs (e.g. file reads, API dumps).
|
|
7
|
+
*/
|
|
8
|
+
export interface StoredResult {
|
|
9
|
+
/** Summary string (or full result if small enough) that goes into context */
|
|
10
|
+
inline: string;
|
|
11
|
+
/** Path to the temp file containing the full result, if spilled */
|
|
12
|
+
fullPath?: string;
|
|
13
|
+
/** Original serialized byte length */
|
|
14
|
+
originalSize: number;
|
|
15
|
+
/** Whether the stored content was truncated */
|
|
16
|
+
truncated: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class ResultStore {
|
|
19
|
+
private results;
|
|
20
|
+
private tempDir;
|
|
21
|
+
constructor(sessionId: string);
|
|
22
|
+
/**
|
|
23
|
+
* Store a tool result. Small results stay inline; large results spill to
|
|
24
|
+
* disk and return a summary pointing to the temp file.
|
|
25
|
+
*/
|
|
26
|
+
store(toolCallId: string, result: unknown): Promise<StoredResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Retrieve a previously stored result by its tool call ID.
|
|
29
|
+
*/
|
|
30
|
+
retrieve(toolCallId: string): StoredResult | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Delete all temp files created during this session.
|
|
33
|
+
*/
|
|
34
|
+
cleanup(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Remove stale result directories (e.g. from crashed runs) older than the TTL.
|
|
37
|
+
* TTL comes from PROFCLAW_RESULT_STORE_TTL_MS (default 24h). Returns the
|
|
38
|
+
* number of directories removed. Never throws.
|
|
39
|
+
*/
|
|
40
|
+
static sweepStale(ttlMs?: number, baseDir?: string, now?: number): Promise<number>;
|
|
41
|
+
private writeTempFile;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=result-store.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result Store
|
|
3
|
+
*
|
|
4
|
+
* Manages large tool results by spilling them to disk when they exceed the
|
|
5
|
+
* inline size limit. This prevents context windows from being flooded by
|
|
6
|
+
* large tool outputs (e.g. file reads, API dumps).
|
|
7
|
+
*/
|
|
8
|
+
import { writeFile, unlink, mkdir, readdir, rm, stat } from "node:fs/promises";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { logger } from "../utils/logger.js";
|
|
13
|
+
/** Results <= 50KB stay inline in the context window */
|
|
14
|
+
const MAX_INLINE_SIZE = 50_000;
|
|
15
|
+
/** Results > 5MB get truncated before saving */
|
|
16
|
+
const MAX_TOTAL_SIZE = 5_000_000;
|
|
17
|
+
/** Number of chars to include in the inline preview */
|
|
18
|
+
const PREVIEW_LENGTH = 500;
|
|
19
|
+
const TEMP_DIR_PREFIX = "profclaw-results-";
|
|
20
|
+
/** Stale temp dir TTL, env-configurable (PROFCLAW_RESULT_STORE_TTL_MS, default 24h) */
|
|
21
|
+
function getResultStoreTtlMs() {
|
|
22
|
+
const parsed = Number.parseInt(process.env.PROFCLAW_RESULT_STORE_TTL_MS ?? "", 10);
|
|
23
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 24 * 60 * 60 * 1000;
|
|
24
|
+
}
|
|
25
|
+
export class ResultStore {
|
|
26
|
+
results = new Map();
|
|
27
|
+
tempDir;
|
|
28
|
+
constructor(sessionId) {
|
|
29
|
+
this.tempDir = join(tmpdir(), `${TEMP_DIR_PREFIX}${sessionId}`);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Store a tool result. Small results stay inline; large results spill to
|
|
33
|
+
* disk and return a summary pointing to the temp file.
|
|
34
|
+
*/
|
|
35
|
+
async store(toolCallId, result) {
|
|
36
|
+
const serialized = JSON.stringify(result);
|
|
37
|
+
const size = Buffer.byteLength(serialized, "utf8");
|
|
38
|
+
let stored;
|
|
39
|
+
if (size <= MAX_INLINE_SIZE) {
|
|
40
|
+
// Small enough — keep it in context as-is
|
|
41
|
+
stored = {
|
|
42
|
+
inline: serialized,
|
|
43
|
+
originalSize: size,
|
|
44
|
+
truncated: false,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Needs to spill to disk
|
|
49
|
+
const truncated = size > MAX_TOTAL_SIZE;
|
|
50
|
+
const content = truncated
|
|
51
|
+
? serialized.slice(0, MAX_TOTAL_SIZE)
|
|
52
|
+
: serialized;
|
|
53
|
+
const filePath = await this.writeTempFile(toolCallId, content);
|
|
54
|
+
const preview = serialized.slice(0, PREVIEW_LENGTH);
|
|
55
|
+
const sizeLabel = truncated
|
|
56
|
+
? `${MAX_TOTAL_SIZE.toLocaleString()} bytes (truncated from ${size.toLocaleString()} bytes)`
|
|
57
|
+
: `${size.toLocaleString()} bytes`;
|
|
58
|
+
stored = {
|
|
59
|
+
inline: `[Result stored: ${sizeLabel}. Use retrieve_result tool to access full data. Preview: ${preview}...]`,
|
|
60
|
+
fullPath: filePath,
|
|
61
|
+
originalSize: size,
|
|
62
|
+
truncated,
|
|
63
|
+
};
|
|
64
|
+
logger.debug("[ResultStore] Spilled large result to disk", {
|
|
65
|
+
toolCallId,
|
|
66
|
+
size,
|
|
67
|
+
truncated,
|
|
68
|
+
filePath,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
this.results.set(toolCallId, stored);
|
|
72
|
+
return stored;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Retrieve a previously stored result by its tool call ID.
|
|
76
|
+
*/
|
|
77
|
+
retrieve(toolCallId) {
|
|
78
|
+
return this.results.get(toolCallId);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Delete all temp files created during this session.
|
|
82
|
+
*/
|
|
83
|
+
async cleanup() {
|
|
84
|
+
const deletions = [];
|
|
85
|
+
for (const [id, stored] of this.results.entries()) {
|
|
86
|
+
if (stored.fullPath) {
|
|
87
|
+
deletions.push(unlink(stored.fullPath).catch((err) => {
|
|
88
|
+
logger.warn("[ResultStore] Failed to delete temp file", {
|
|
89
|
+
id,
|
|
90
|
+
path: stored.fullPath,
|
|
91
|
+
error: err instanceof Error ? err.message : String(err),
|
|
92
|
+
});
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
await Promise.all(deletions);
|
|
97
|
+
this.results.clear();
|
|
98
|
+
logger.debug("[ResultStore] Cleaned up temp files", {
|
|
99
|
+
count: deletions.length,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Remove stale result directories (e.g. from crashed runs) older than the TTL.
|
|
104
|
+
* TTL comes from PROFCLAW_RESULT_STORE_TTL_MS (default 24h). Returns the
|
|
105
|
+
* number of directories removed. Never throws.
|
|
106
|
+
*/
|
|
107
|
+
static async sweepStale(ttlMs = getResultStoreTtlMs(), baseDir = tmpdir(), now = Date.now()) {
|
|
108
|
+
let removed = 0;
|
|
109
|
+
try {
|
|
110
|
+
const entries = await readdir(baseDir);
|
|
111
|
+
for (const name of entries) {
|
|
112
|
+
if (!name.startsWith(TEMP_DIR_PREFIX))
|
|
113
|
+
continue;
|
|
114
|
+
const dir = join(baseDir, name);
|
|
115
|
+
try {
|
|
116
|
+
const info = await stat(dir);
|
|
117
|
+
if (!info.isDirectory() || now - info.mtimeMs <= ttlMs)
|
|
118
|
+
continue;
|
|
119
|
+
await rm(dir, { recursive: true, force: true });
|
|
120
|
+
removed++;
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
logger.warn("[ResultStore] Failed to sweep stale dir", {
|
|
124
|
+
dir,
|
|
125
|
+
error: err instanceof Error ? err.message : String(err),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
logger.warn("[ResultStore] Stale sweep failed", {
|
|
132
|
+
error: err instanceof Error ? err.message : String(err),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return removed;
|
|
136
|
+
}
|
|
137
|
+
async writeTempFile(toolCallId, content) {
|
|
138
|
+
await mkdir(this.tempDir, { recursive: true });
|
|
139
|
+
const fileName = `${toolCallId}-${randomUUID()}.json`;
|
|
140
|
+
const filePath = join(this.tempDir, fileName);
|
|
141
|
+
await writeFile(filePath, content, "utf8");
|
|
142
|
+
return filePath;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=result-store.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Diff Tracker
|
|
3
|
+
*
|
|
4
|
+
* Records original file contents before the agent makes any edits so that
|
|
5
|
+
* a unified diff of all changes can be produced at any time (e.g. /diff).
|
|
6
|
+
*/
|
|
7
|
+
export type FileChangeStatus = 'modified' | 'created' | 'deleted';
|
|
8
|
+
export declare class SessionDiffTracker {
|
|
9
|
+
private originals;
|
|
10
|
+
constructor();
|
|
11
|
+
/**
|
|
12
|
+
* Record the original content of a file before the agent touches it.
|
|
13
|
+
* Only the first call per file path is honoured — subsequent calls are
|
|
14
|
+
* ignored so the "before" state remains the pre-session state.
|
|
15
|
+
*/
|
|
16
|
+
recordOriginal(filePath: string, content: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Record that a file was created during this session (it did not exist before).
|
|
19
|
+
*/
|
|
20
|
+
recordCreated(filePath: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Generate a unified diff for all modified files.
|
|
23
|
+
* Returns an empty string if nothing has changed.
|
|
24
|
+
*/
|
|
25
|
+
generateDiff(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Return a list of changed files with their status.
|
|
28
|
+
*/
|
|
29
|
+
getChangedFiles(): Array<{
|
|
30
|
+
path: string;
|
|
31
|
+
status: FileChangeStatus;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Generate a unified diff for a single file.
|
|
35
|
+
* Returns an empty string if the file content is unchanged.
|
|
36
|
+
*/
|
|
37
|
+
getFileDiff(filePath: string): Promise<string>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns the process-level singleton SessionDiffTracker.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getSessionDiffTracker(): SessionDiffTracker;
|
|
43
|
+
/**
|
|
44
|
+
* Produce a minimal edit script using the Myers diff algorithm.
|
|
45
|
+
* Returns an array of [type, line] tuples where type is '+', '-', or '='.
|
|
46
|
+
*/
|
|
47
|
+
/** Myers shortest edit script. Exported for tests. */
|
|
48
|
+
export declare function shortestEditScript(oldLines: string[], newLines: string[]): Array<['+' | '-' | '=', string]>;
|
|
49
|
+
//# sourceMappingURL=session-diff.d.ts.map
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Diff Tracker
|
|
3
|
+
*
|
|
4
|
+
* Records original file contents before the agent makes any edits so that
|
|
5
|
+
* a unified diff of all changes can be produced at any time (e.g. /diff).
|
|
6
|
+
*/
|
|
7
|
+
import fs from 'node:fs/promises';
|
|
8
|
+
let defaultTracker = null;
|
|
9
|
+
export class SessionDiffTracker {
|
|
10
|
+
originals;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.originals = new Map();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Record the original content of a file before the agent touches it.
|
|
16
|
+
* Only the first call per file path is honoured — subsequent calls are
|
|
17
|
+
* ignored so the "before" state remains the pre-session state.
|
|
18
|
+
*/
|
|
19
|
+
recordOriginal(filePath, content) {
|
|
20
|
+
if (!this.originals.has(filePath)) {
|
|
21
|
+
this.originals.set(filePath, { originalContent: content, created: false });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Record that a file was created during this session (it did not exist before).
|
|
26
|
+
*/
|
|
27
|
+
recordCreated(filePath) {
|
|
28
|
+
if (!this.originals.has(filePath)) {
|
|
29
|
+
this.originals.set(filePath, { originalContent: null, created: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate a unified diff for all modified files.
|
|
34
|
+
* Returns an empty string if nothing has changed.
|
|
35
|
+
*/
|
|
36
|
+
async generateDiff() {
|
|
37
|
+
const parts = [];
|
|
38
|
+
for (const filePath of this.originals.keys()) {
|
|
39
|
+
const diff = await this.getFileDiff(filePath);
|
|
40
|
+
if (diff) {
|
|
41
|
+
parts.push(diff);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return parts.join('\n');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Return a list of changed files with their status.
|
|
48
|
+
*/
|
|
49
|
+
getChangedFiles() {
|
|
50
|
+
const result = [];
|
|
51
|
+
for (const [filePath, tracked] of this.originals.entries()) {
|
|
52
|
+
if (tracked.created) {
|
|
53
|
+
result.push({ path: filePath, status: 'created' });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
result.push({ path: filePath, status: 'modified' });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate a unified diff for a single file.
|
|
63
|
+
* Returns an empty string if the file content is unchanged.
|
|
64
|
+
*/
|
|
65
|
+
async getFileDiff(filePath) {
|
|
66
|
+
const tracked = this.originals.get(filePath);
|
|
67
|
+
if (!tracked)
|
|
68
|
+
return '';
|
|
69
|
+
let currentContent;
|
|
70
|
+
try {
|
|
71
|
+
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
currentContent = null;
|
|
75
|
+
}
|
|
76
|
+
const originalContent = tracked.originalContent ?? '';
|
|
77
|
+
const newContent = currentContent ?? '';
|
|
78
|
+
if (originalContent === newContent)
|
|
79
|
+
return '';
|
|
80
|
+
const aLabel = tracked.created ? '/dev/null' : `a/${filePath}`;
|
|
81
|
+
const bLabel = currentContent === null ? '/dev/null' : `b/${filePath}`;
|
|
82
|
+
return buildUnifiedDiff(aLabel, bLabel, originalContent, newContent);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns the process-level singleton SessionDiffTracker.
|
|
87
|
+
*/
|
|
88
|
+
export function getSessionDiffTracker() {
|
|
89
|
+
if (!defaultTracker) {
|
|
90
|
+
defaultTracker = new SessionDiffTracker();
|
|
91
|
+
}
|
|
92
|
+
return defaultTracker;
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Minimal unified diff implementation (no external dependencies)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
function buildUnifiedDiff(aLabel, bLabel, oldText, newText) {
|
|
98
|
+
const oldLines = oldText === '' ? [] : oldText.split('\n');
|
|
99
|
+
const newLines = newText === '' ? [] : newText.split('\n');
|
|
100
|
+
const hunks = computeHunks(oldLines, newLines);
|
|
101
|
+
if (hunks.length === 0)
|
|
102
|
+
return '';
|
|
103
|
+
const header = [`--- ${aLabel}`, `+++ ${bLabel}`];
|
|
104
|
+
const body = hunks.map(renderHunk);
|
|
105
|
+
return [...header, ...body].join('\n') + '\n';
|
|
106
|
+
}
|
|
107
|
+
const CONTEXT = 3;
|
|
108
|
+
function computeHunks(oldLines, newLines) {
|
|
109
|
+
// Myers-style LCS diff
|
|
110
|
+
const edit = shortestEditScript(oldLines, newLines);
|
|
111
|
+
// Group edits into hunks with context
|
|
112
|
+
const hunks = [];
|
|
113
|
+
// Build flat edit list
|
|
114
|
+
const ops = [];
|
|
115
|
+
let oi = 0;
|
|
116
|
+
let ni = 0;
|
|
117
|
+
for (const [type, content] of edit) {
|
|
118
|
+
if (type === '=') {
|
|
119
|
+
ops.push({ type: '=', oldIdx: oi, newIdx: ni, content });
|
|
120
|
+
oi++;
|
|
121
|
+
ni++;
|
|
122
|
+
}
|
|
123
|
+
else if (type === '-') {
|
|
124
|
+
ops.push({ type: '-', oldIdx: oi, newIdx: ni, content });
|
|
125
|
+
oi++;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
ops.push({ type: '+', oldIdx: oi, newIdx: ni, content });
|
|
129
|
+
ni++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Find change ranges
|
|
133
|
+
const changeIndices = ops
|
|
134
|
+
.map((op, i) => (op.type !== '=' ? i : -1))
|
|
135
|
+
.filter((i) => i !== -1);
|
|
136
|
+
if (changeIndices.length === 0)
|
|
137
|
+
return [];
|
|
138
|
+
// Merge ranges with context into hunks
|
|
139
|
+
let start = Math.max(0, changeIndices[0] - CONTEXT);
|
|
140
|
+
let end = Math.min(ops.length - 1, changeIndices[0] + CONTEXT);
|
|
141
|
+
const ranges = [];
|
|
142
|
+
for (let k = 1; k < changeIndices.length; k++) {
|
|
143
|
+
const ci = changeIndices[k];
|
|
144
|
+
if (ci - CONTEXT <= end + 1) {
|
|
145
|
+
end = Math.min(ops.length - 1, ci + CONTEXT);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
ranges.push([start, end]);
|
|
149
|
+
start = Math.max(0, ci - CONTEXT);
|
|
150
|
+
end = Math.min(ops.length - 1, ci + CONTEXT);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
ranges.push([start, end]);
|
|
154
|
+
for (const [s, e] of ranges) {
|
|
155
|
+
const slice = ops.slice(s, e + 1);
|
|
156
|
+
const oldStart = (slice.find((op) => op.type !== '+')?.oldIdx ?? 0) + 1;
|
|
157
|
+
const newStart = (slice.find((op) => op.type !== '-')?.newIdx ?? 0) + 1;
|
|
158
|
+
const oldCount = slice.filter((op) => op.type !== '+').length;
|
|
159
|
+
const newCount = slice.filter((op) => op.type !== '-').length;
|
|
160
|
+
const header = `@@ -${oldStart},${oldCount} +${newStart},${newCount} @@`;
|
|
161
|
+
const lines = slice.map((op) => {
|
|
162
|
+
if (op.type === '=')
|
|
163
|
+
return ` ${op.content}`;
|
|
164
|
+
if (op.type === '-')
|
|
165
|
+
return `-${op.content}`;
|
|
166
|
+
return `+${op.content}`;
|
|
167
|
+
});
|
|
168
|
+
hunks.push({ header, lines });
|
|
169
|
+
}
|
|
170
|
+
return hunks;
|
|
171
|
+
}
|
|
172
|
+
function renderHunk(hunk) {
|
|
173
|
+
return [hunk.header, ...hunk.lines].join('\n');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Produce a minimal edit script using the Myers diff algorithm.
|
|
177
|
+
* Returns an array of [type, line] tuples where type is '+', '-', or '='.
|
|
178
|
+
*/
|
|
179
|
+
/** Myers shortest edit script. Exported for tests. */
|
|
180
|
+
export function shortestEditScript(oldLines, newLines) {
|
|
181
|
+
const n = oldLines.length;
|
|
182
|
+
const m = newLines.length;
|
|
183
|
+
const max = n + m;
|
|
184
|
+
if (max === 0)
|
|
185
|
+
return [];
|
|
186
|
+
// v[k] stores the furthest-reaching x position for diagonal k
|
|
187
|
+
const v = new Array(2 * max + 1).fill(0);
|
|
188
|
+
const trace = [];
|
|
189
|
+
outer: for (let d = 0; d <= max; d++) {
|
|
190
|
+
trace.push([...v]);
|
|
191
|
+
for (let k = -d; k <= d; k += 2) {
|
|
192
|
+
const ki = k + max;
|
|
193
|
+
let x;
|
|
194
|
+
if (k === -d || (k !== d && v[ki - 1] < v[ki + 1])) {
|
|
195
|
+
x = v[ki + 1];
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
x = v[ki - 1] + 1;
|
|
199
|
+
}
|
|
200
|
+
let y = x - k;
|
|
201
|
+
while (x < n && y < m && oldLines[x] === newLines[y]) {
|
|
202
|
+
x++;
|
|
203
|
+
y++;
|
|
204
|
+
}
|
|
205
|
+
v[ki] = x;
|
|
206
|
+
if (x >= n && y >= m) {
|
|
207
|
+
break outer;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Backtrack to reconstruct the edit path
|
|
212
|
+
const edits = [];
|
|
213
|
+
let x = n;
|
|
214
|
+
let y = m;
|
|
215
|
+
for (let d = trace.length - 1; d >= 0; d--) {
|
|
216
|
+
const vPrev = trace[d];
|
|
217
|
+
const k = x - y;
|
|
218
|
+
const down = k === -d || (k !== d && vPrev[k - 1 + max] < vPrev[k + 1 + max]);
|
|
219
|
+
const prevK = down ? k + 1 : k - 1;
|
|
220
|
+
const prevX = vPrev[prevK + max];
|
|
221
|
+
const prevY = prevX - prevK;
|
|
222
|
+
// Follow the diagonal (matching lines) back to where this round's edit ended.
|
|
223
|
+
while (x > prevX && y > prevY) {
|
|
224
|
+
edits.unshift(['=', oldLines[x - 1]]);
|
|
225
|
+
x--;
|
|
226
|
+
y--;
|
|
227
|
+
}
|
|
228
|
+
if (d > 0) {
|
|
229
|
+
if (down) {
|
|
230
|
+
edits.unshift(['+', newLines[y - 1]]);
|
|
231
|
+
y--;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
edits.unshift(['-', oldLines[x - 1]]);
|
|
235
|
+
x--;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
while (x > 0) {
|
|
240
|
+
edits.unshift(['-', oldLines[x - 1]]);
|
|
241
|
+
x--;
|
|
242
|
+
}
|
|
243
|
+
while (y > 0) {
|
|
244
|
+
edits.unshift(['+', newLines[y - 1]]);
|
|
245
|
+
y--;
|
|
246
|
+
}
|
|
247
|
+
return edits;
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=session-diff.js.map
|