@epoch-ai/cli 2.2.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/.artifacts/unit/junit.xml +2823 -0
- package/.project-map/backups/20260530_223453/.project-map.json +90101 -0
- package/.project-map/backups/20260530_223507/.project-map.json +90101 -0
- package/.project-map/backups/20260530_223512/.project-map.json +90101 -0
- package/.project-map/backups/20260530_223512/map.toon +666 -0
- package/.project-map/backups/20260530_223516/.project-map.json +90101 -0
- package/.project-map/backups/20260530_223516/map.toon +666 -0
- package/.project-map/backups/20260530_223520/.project-map.json +90101 -0
- package/.project-map/backups/20260530_223520/map.toon +666 -0
- package/AGENTS.md +47 -0
- package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/epochcli +179 -0
- package/bunfig.toml +7 -0
- package/drizzle.config.ts +10 -0
- package/git +0 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
- package/migration/20260211171708_add_project_commands/migration.sql +1 -0
- package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
- package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
- package/migration/20260225215848_workspace/migration.sql +7 -0
- package/migration/20260225215848_workspace/snapshot.json +959 -0
- package/migration/20260227213759_add_session_workspace_id/migration.sql +2 -0
- package/migration/20260227213759_add_session_workspace_id/snapshot.json +983 -0
- package/migration/20260228203230_blue_harpoon/migration.sql +17 -0
- package/migration/20260228203230_blue_harpoon/snapshot.json +1102 -0
- package/migration/20260303231226_add_workspace_fields/migration.sql +5 -0
- package/migration/20260303231226_add_workspace_fields/snapshot.json +1013 -0
- package/migration/20260309230000_move_org_to_state/migration.sql +3 -0
- package/migration/20260309230000_move_org_to_state/snapshot.json +1156 -0
- package/migration/20260312043431_session_message_cursor/migration.sql +4 -0
- package/migration/20260312043431_session_message_cursor/snapshot.json +1168 -0
- package/migration/20260323234822_events/migration.sql +13 -0
- package/migration/20260323234822_events/snapshot.json +1271 -0
- package/migration/20260418092949_add_yolo_to_session/migration.sql +2 -0
- package/migration/20260418092949_add_yolo_to_session/snapshot.json +1199 -0
- package/migration/20260419120000_add_intervention_to_session/migration.sql +2 -0
- package/package.json +186 -0
- package/parsers-config.ts +290 -0
- package/script/build-node.ts +71 -0
- package/script/build.ts +255 -0
- package/script/check-migrations.ts +16 -0
- package/script/fix-node-pty.ts +28 -0
- package/script/postinstall.mjs +131 -0
- package/script/publish.ts +184 -0
- package/script/schema.ts +63 -0
- package/script/seed-e2e.ts +60 -0
- package/script/upgrade-opentui.ts +64 -0
- package/specs/effect-migration.md +310 -0
- package/specs/tui-plugins.md +436 -0
- package/specs/v2.md +14 -0
- package/src/account/account.sql.ts +39 -0
- package/src/account/index.ts +465 -0
- package/src/account/repo.ts +163 -0
- package/src/account/schema.ts +91 -0
- package/src/acp/README.md +174 -0
- package/src/acp/agent.ts +1847 -0
- package/src/acp/session.ts +116 -0
- package/src/acp/types.ts +24 -0
- package/src/agent/agent.ts +445 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +15 -0
- package/src/agent/prompt/explore.txt +9 -0
- package/src/agent/prompt/summary.txt +11 -0
- package/src/agent/prompt/title.txt +44 -0
- package/src/auth/index.ts +110 -0
- package/src/bus/bus-event.ts +40 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +232 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/account.ts +257 -0
- package/src/cli/cmd/acp.ts +70 -0
- package/src/cli/cmd/agent.ts +245 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/db.ts +119 -0
- package/src/cli/cmd/debug/agent.ts +167 -0
- package/src/cli/cmd/debug/config.ts +16 -0
- package/src/cli/cmd/debug/file.ts +97 -0
- package/src/cli/cmd/debug/index.ts +48 -0
- package/src/cli/cmd/debug/lsp.ts +53 -0
- package/src/cli/cmd/debug/ripgrep.ts +87 -0
- package/src/cli/cmd/debug/scrap.ts +16 -0
- package/src/cli/cmd/debug/skill.ts +16 -0
- package/src/cli/cmd/debug/snapshot.ts +52 -0
- package/src/cli/cmd/export.ts +89 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1639 -0
- package/src/cli/cmd/import.ts +169 -0
- package/src/cli/cmd/mcp.ts +754 -0
- package/src/cli/cmd/models.ts +78 -0
- package/src/cli/cmd/plug.ts +233 -0
- package/src/cli/cmd/pr.ts +127 -0
- package/src/cli/cmd/providers.ts +478 -0
- package/src/cli/cmd/run.ts +681 -0
- package/src/cli/cmd/serve.ts +24 -0
- package/src/cli/cmd/session.ts +159 -0
- package/src/cli/cmd/stats.ts +410 -0
- package/src/cli/cmd/tui/app.tsx +945 -0
- package/src/cli/cmd/tui/attach.ts +88 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +171 -0
- package/src/cli/cmd/tui/component/dialog-console-org.tsx +103 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +190 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +364 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +168 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/dialog-variant.tsx +39 -0
- package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +320 -0
- package/src/cli/cmd/tui/component/error-component.tsx +92 -0
- package/src/cli/cmd/tui/component/logo.tsx +85 -0
- package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +672 -0
- package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +109 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1336 -0
- package/src/cli/cmd/tui/component/prompt/part.ts +16 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/spinner.tsx +24 -0
- package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
- package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/component/workspace/dialog-session-list.tsx +151 -0
- package/src/cli/cmd/tui/context/args.tsx +15 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +60 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +105 -0
- package/src/cli/cmd/tui/context/kv.tsx +52 -0
- package/src/cli/cmd/tui/context/local.tsx +456 -0
- package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +52 -0
- package/src/cli/cmd/tui/context/sdk.tsx +115 -0
- package/src/cli/cmd/tui/context/sync.tsx +516 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/epochcli.json +245 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +249 -0
- package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1236 -0
- package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
- package/src/cli/cmd/tui/event.ts +48 -0
- package/src/cli/cmd/tui/feature-plugins/home/footer.tsx +93 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +145 -0
- package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
- package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
- package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +270 -0
- package/src/cli/cmd/tui/plugin/api.tsx +397 -0
- package/src/cli/cmd/tui/plugin/index.ts +3 -0
- package/src/cli/cmd/tui/plugin/internal.ts +27 -0
- package/src/cli/cmd/tui/plugin/runtime.ts +1031 -0
- package/src/cli/cmd/tui/plugin/slots.tsx +60 -0
- package/src/cli/cmd/tui/routes/home.tsx +84 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +2161 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +691 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +468 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +70 -0
- package/src/cli/cmd/tui/routes/session/subagent-footer.tsx +131 -0
- package/src/cli/cmd/tui/thread.ts +241 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +89 -0
- package/src/cli/cmd/tui/ui/dialog-export-options.tsx +211 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +115 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +417 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +192 -0
- package/src/cli/cmd/tui/ui/link.tsx +28 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +192 -0
- package/src/cli/cmd/tui/util/editor.ts +37 -0
- package/src/cli/cmd/tui/util/model.ts +23 -0
- package/src/cli/cmd/tui/util/provider-origin.ts +20 -0
- package/src/cli/cmd/tui/util/scroll.ts +23 -0
- package/src/cli/cmd/tui/util/selection.ts +25 -0
- package/src/cli/cmd/tui/util/signal.ts +7 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/util/transcript.ts +112 -0
- package/src/cli/cmd/tui/win32.ts +129 -0
- package/src/cli/cmd/tui/worker.ts +195 -0
- package/src/cli/cmd/uninstall.ts +353 -0
- package/src/cli/cmd/upgrade.ts +73 -0
- package/src/cli/cmd/web.ts +81 -0
- package/src/cli/effect/prompt.ts +25 -0
- package/src/cli/error.ts +46 -0
- package/src/cli/heap.ts +59 -0
- package/src/cli/logo.ts +6 -0
- package/src/cli/network.ts +60 -0
- package/src/cli/ui.ts +133 -0
- package/src/cli/upgrade.ts +31 -0
- package/src/command/index.ts +197 -0
- package/src/command/template/initialize.txt +66 -0
- package/src/command/template/review.txt +101 -0
- package/src/config/config.ts +1610 -0
- package/src/config/console-state.ts +15 -0
- package/src/config/markdown.ts +99 -0
- package/src/config/paths.ts +167 -0
- package/src/config/tui-migrate.ts +155 -0
- package/src/config/tui-schema.ts +37 -0
- package/src/config/tui.ts +179 -0
- package/src/config/validator.ts +52 -0
- package/src/control-plane/adaptors/index.ts +20 -0
- package/src/control-plane/adaptors/worktree.ts +42 -0
- package/src/control-plane/schema.ts +17 -0
- package/src/control-plane/sse.ts +66 -0
- package/src/control-plane/types.ts +32 -0
- package/src/control-plane/workspace.sql.ts +17 -0
- package/src/control-plane/workspace.ts +168 -0
- package/src/effect/cross-spawn-spawner.ts +502 -0
- package/src/effect/instance-ref.ts +6 -0
- package/src/effect/instance-registry.ts +12 -0
- package/src/effect/instance-state.ts +82 -0
- package/src/effect/run-service.ts +33 -0
- package/src/effect/runner.ts +216 -0
- package/src/env/index.ts +28 -0
- package/src/file/ignore.ts +82 -0
- package/src/file/index.ts +686 -0
- package/src/file/protected.ts +59 -0
- package/src/file/ripgrep.ts +376 -0
- package/src/file/time.ts +133 -0
- package/src/file/watcher.ts +172 -0
- package/src/filesystem/index.ts +236 -0
- package/src/flag/flag.ts +157 -0
- package/src/format/formatter.ts +413 -0
- package/src/format/index.ts +203 -0
- package/src/git/index.ts +303 -0
- package/src/global/index.ts +54 -0
- package/src/id/id.ts +85 -0
- package/src/ide/index.ts +74 -0
- package/src/index.ts +253 -0
- package/src/installation/index.ts +355 -0
- package/src/installation/meta.ts +7 -0
- package/src/lsp/client.ts +256 -0
- package/src/lsp/index.ts +558 -0
- package/src/lsp/language.ts +120 -0
- package/src/lsp/launch.ts +21 -0
- package/src/lsp/server.ts +1968 -0
- package/src/mcp/auth.ts +173 -0
- package/src/mcp/index.ts +1250 -0
- package/src/mcp/oauth-callback.ts +216 -0
- package/src/mcp/oauth-provider.ts +185 -0
- package/src/mcp/schema-loader.ts +82 -0
- package/src/node.ts +1 -0
- package/src/npm/index.ts +188 -0
- package/src/patch/index.ts +680 -0
- package/src/permission/arity.ts +163 -0
- package/src/permission/evaluate.ts +15 -0
- package/src/permission/index.ts +323 -0
- package/src/permission/schema.ts +17 -0
- package/src/plugin/cloudflare.ts +67 -0
- package/src/plugin/codex.ts +608 -0
- package/src/plugin/github-copilot/copilot.ts +361 -0
- package/src/plugin/github-copilot/models.ts +144 -0
- package/src/plugin/index.ts +288 -0
- package/src/plugin/install.ts +439 -0
- package/src/plugin/loader.ts +174 -0
- package/src/plugin/meta.ts +188 -0
- package/src/plugin/shared.ts +323 -0
- package/src/project/bootstrap.ts +29 -0
- package/src/project/instance.ts +175 -0
- package/src/project/project.sql.ts +16 -0
- package/src/project/project.ts +519 -0
- package/src/project/schema.ts +16 -0
- package/src/project/state.ts +70 -0
- package/src/project/vcs.ts +240 -0
- package/src/provider/auth.ts +253 -0
- package/src/provider/error.ts +297 -0
- package/src/provider/models.ts +162 -0
- package/src/provider/provider.ts +1776 -0
- package/src/provider/schema.ts +38 -0
- package/src/provider/sdk/copilot/README.md +5 -0
- package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +170 -0
- package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +814 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +83 -0
- package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
- package/src/provider/sdk/copilot/index.ts +2 -0
- package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
- package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
- package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
- package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
- package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1769 -0
- package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
- package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
- package/src/provider/sdk/copilot/responses/tool/file-search.ts +127 -0
- package/src/provider/sdk/copilot/responses/tool/image-generation.ts +114 -0
- package/src/provider/sdk/copilot/responses/tool/local-shell.ts +64 -0
- package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
- package/src/provider/sdk/copilot/responses/tool/web-search.ts +102 -0
- package/src/provider/transform.ts +1124 -0
- package/src/pty/index.ts +397 -0
- package/src/pty/pty.bun.ts +26 -0
- package/src/pty/pty.node.ts +27 -0
- package/src/pty/pty.ts +25 -0
- package/src/pty/schema.ts +17 -0
- package/src/question/index.ts +224 -0
- package/src/question/schema.ts +17 -0
- package/src/server/error.ts +36 -0
- package/src/server/event.ts +7 -0
- package/src/server/instance.ts +315 -0
- package/src/server/mdns.ts +60 -0
- package/src/server/middleware.ts +33 -0
- package/src/server/projectors.ts +28 -0
- package/src/server/proxy.ts +130 -0
- package/src/server/router.ts +105 -0
- package/src/server/routes/config.ts +92 -0
- package/src/server/routes/event.ts +83 -0
- package/src/server/routes/experimental.ts +374 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +312 -0
- package/src/server/routes/mcp.ts +225 -0
- package/src/server/routes/permission.ts +69 -0
- package/src/server/routes/project.ts +118 -0
- package/src/server/routes/provider.ts +171 -0
- package/src/server/routes/pty.ts +210 -0
- package/src/server/routes/question.ts +99 -0
- package/src/server/routes/session.ts +984 -0
- package/src/server/routes/tui.ts +378 -0
- package/src/server/routes/workspace.ts +94 -0
- package/src/server/server.ts +353 -0
- package/src/session/compaction.ts +86 -0
- package/src/session/index.ts +904 -0
- package/src/session/instruction.ts +261 -0
- package/src/session/llm/monitor.ts +87 -0
- package/src/session/llm.ts +1676 -0
- package/src/session/message-v2.ts +1082 -0
- package/src/session/message.ts +191 -0
- package/src/session/overflow.ts +35 -0
- package/src/session/processor.ts +635 -0
- package/src/session/projectors.ts +136 -0
- package/src/session/prompt/build-switch.txt +5 -0
- package/src/session/prompt/builder.ts +135 -0
- package/src/session/prompt/default.txt +11 -0
- package/src/session/prompt/engine.ts +1072 -0
- package/src/session/prompt/gemma4.txt +1 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/orchestrator.ts +426 -0
- package/src/session/prompt/plan.txt +28 -0
- package/src/session/prompt/qwen.txt +19 -0
- package/src/session/prompt/resolver.ts +670 -0
- package/src/session/prompt/router.ts +197 -0
- package/src/session/prompt/state.ts +96 -0
- package/src/session/prompt/types.ts +115 -0
- package/src/session/prompt/utils.ts +15 -0
- package/src/session/prompt.ts +362 -0
- package/src/session/retry.ts +106 -0
- package/src/session/revert.ts +176 -0
- package/src/session/sanitizer.ts +125 -0
- package/src/session/schema.ts +38 -0
- package/src/session/session.sql.ts +106 -0
- package/src/session/status.ts +102 -0
- package/src/session/summary.ts +183 -0
- package/src/session/system.ts +79 -0
- package/src/session/todo.ts +166 -0
- package/src/session/worker.ts +382 -0
- package/src/shell/shell.ts +110 -0
- package/src/skill/discovery.ts +116 -0
- package/src/skill/index.ts +287 -0
- package/src/snapshot/index.ts +726 -0
- package/src/sql.d.ts +4 -0
- package/src/storage/db.bun.ts +8 -0
- package/src/storage/db.node.ts +8 -0
- package/src/storage/db.ts +174 -0
- package/src/storage/json-migration.ts +387 -0
- package/src/storage/schema.sql.ts +10 -0
- package/src/storage/schema.ts +4 -0
- package/src/storage/storage.ts +353 -0
- package/src/sync/README.md +179 -0
- package/src/sync/event.sql.ts +16 -0
- package/src/sync/index.ts +263 -0
- package/src/sync/schema.ts +14 -0
- package/src/tool/apply_patch.ts +281 -0
- package/src/tool/apply_patch.txt +1 -0
- package/src/tool/arbitration.txt +5 -0
- package/src/tool/bash.ts +494 -0
- package/src/tool/bash.txt +2 -0
- package/src/tool/batch.ts +183 -0
- package/src/tool/batch.txt +1 -0
- package/src/tool/codesearch.ts +132 -0
- package/src/tool/codesearch.txt +1 -0
- package/src/tool/edit.ts +734 -0
- package/src/tool/edit.txt +1 -0
- package/src/tool/external-directory.ts +46 -0
- package/src/tool/glob.ts +73 -0
- package/src/tool/glob.txt +2 -0
- package/src/tool/grep.ts +156 -0
- package/src/tool/grep.txt +2 -0
- package/src/tool/invalid.ts +20 -0
- package/src/tool/ls.ts +121 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp.ts +97 -0
- package/src/tool/lsp.txt +1 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +1 -0
- package/src/tool/plan-enter.txt +14 -0
- package/src/tool/plan-exit.txt +13 -0
- package/src/tool/plan.ts +131 -0
- package/src/tool/question.ts +46 -0
- package/src/tool/question.txt +10 -0
- package/src/tool/read.ts +332 -0
- package/src/tool/read.txt +1 -0
- package/src/tool/registry.ts +288 -0
- package/src/tool/revert.ts +37 -0
- package/src/tool/schema.ts +17 -0
- package/src/tool/skill.ts +105 -0
- package/src/tool/task.ts +150 -0
- package/src/tool/task.txt +3 -0
- package/src/tool/task_complete.ts +21 -0
- package/src/tool/tool.ts +112 -0
- package/src/tool/truncate.ts +144 -0
- package/src/tool/truncation-dir.ts +4 -0
- package/src/tool/webfetch.ts +206 -0
- package/src/tool/webfetch.txt +1 -0
- package/src/tool/websearch.ts +150 -0
- package/src/tool/websearch.txt +1 -0
- package/src/tool/write.ts +101 -0
- package/src/tool/write.txt +1 -0
- package/src/util/abort.ts +35 -0
- package/src/util/ai-sdk.ts +59 -0
- package/src/util/archive.ts +17 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/data-url.ts +9 -0
- package/src/util/defer.ts +12 -0
- package/src/util/effect-http-client.ts +11 -0
- package/src/util/effect-zod.ts +98 -0
- package/src/util/error.ts +77 -0
- package/src/util/filesystem.ts +245 -0
- package/src/util/flock.ts +333 -0
- package/src/util/fn.ts +21 -0
- package/src/util/format.ts +20 -0
- package/src/util/glob.ts +34 -0
- package/src/util/hash.ts +7 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +103 -0
- package/src/util/lazy.ts +23 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log-parser.ts +114 -0
- package/src/util/log.ts +250 -0
- package/src/util/network.ts +23 -0
- package/src/util/process.ts +176 -0
- package/src/util/queue.ts +32 -0
- package/src/util/record.ts +3 -0
- package/src/util/rpc.ts +66 -0
- package/src/util/schema.ts +53 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/session-analyzer.ts +331 -0
- package/src/util/session-telemetry.ts +91 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/tokenizer.ts +50 -0
- package/src/util/toon.ts +45 -0
- package/src/util/update-schema.ts +13 -0
- package/src/util/which.ts +14 -0
- package/src/util/wildcard.ts +59 -0
- package/src/worktree/index.ts +612 -0
- package/sst-env.d.ts +10 -0
- package/test/AGENTS.md +81 -0
- package/test/account/repo.test.ts +326 -0
- package/test/account/service.test.ts +393 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +685 -0
- package/test/agent/agent.test.ts +716 -0
- package/test/auth/auth.test.ts +58 -0
- package/test/bus/bus-effect.test.ts +164 -0
- package/test/bus/bus-integration.test.ts +87 -0
- package/test/bus/bus.test.ts +219 -0
- package/test/cli/account.test.ts +26 -0
- package/test/cli/cmd/tui/prompt-part.test.ts +47 -0
- package/test/cli/github-action.test.ts +198 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/cli/plugin-auth-picker.test.ts +120 -0
- package/test/cli/tui/keybind-plugin.test.ts +90 -0
- package/test/cli/tui/plugin-add.test.ts +107 -0
- package/test/cli/tui/plugin-install.test.ts +89 -0
- package/test/cli/tui/plugin-lifecycle.test.ts +225 -0
- package/test/cli/tui/plugin-loader-entrypoint.test.ts +492 -0
- package/test/cli/tui/plugin-loader-pure.test.ts +72 -0
- package/test/cli/tui/plugin-loader.test.ts +752 -0
- package/test/cli/tui/plugin-toggle.test.ts +159 -0
- package/test/cli/tui/slot-replace.test.tsx +47 -0
- package/test/cli/tui/theme-store.test.ts +51 -0
- package/test/cli/tui/thread.test.ts +128 -0
- package/test/cli/tui/transcript.test.ts +426 -0
- package/test/config/agent-color.test.ts +71 -0
- package/test/config/config.test.ts +2337 -0
- package/test/config/fixtures/empty-frontmatter.md +4 -0
- package/test/config/fixtures/frontmatter.md +28 -0
- package/test/config/fixtures/markdown-header.md +11 -0
- package/test/config/fixtures/no-frontmatter.md +1 -0
- package/test/config/fixtures/weird-model-id.md +13 -0
- package/test/config/markdown.test.ts +228 -0
- package/test/config/tui.test.ts +800 -0
- package/test/control-plane/sse.test.ts +56 -0
- package/test/effect/cross-spawn-spawner.test.ts +412 -0
- package/test/effect/instance-state.test.ts +482 -0
- package/test/effect/run-service.test.ts +46 -0
- package/test/effect/runner.test.ts +523 -0
- package/test/fake/provider.ts +82 -0
- package/test/file/fsmonitor.test.ts +62 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/index.test.ts +946 -0
- package/test/file/path-traversal.test.ts +198 -0
- package/test/file/ripgrep.test.ts +54 -0
- package/test/file/time.test.ts +445 -0
- package/test/file/watcher.test.ts +247 -0
- package/test/filesystem/filesystem.test.ts +319 -0
- package/test/fixture/db.ts +11 -0
- package/test/fixture/fixture.test.ts +26 -0
- package/test/fixture/fixture.ts +172 -0
- package/test/fixture/flock-worker.ts +72 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/fixture/plug-worker.ts +93 -0
- package/test/fixture/plugin-meta-worker.ts +26 -0
- package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
- package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
- package/test/fixture/skills/cloudflare/SKILL.md +211 -0
- package/test/fixture/skills/index.json +6 -0
- package/test/fixture/tui-plugin.ts +328 -0
- package/test/fixture/tui-runtime.ts +27 -0
- package/test/format/format.test.ts +171 -0
- package/test/git/git.test.ts +128 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/installation/installation.test.ts +152 -0
- package/test/keybind.test.ts +421 -0
- package/test/lib/effect.ts +53 -0
- package/test/lib/filesystem.ts +10 -0
- package/test/lib/llm-server.ts +794 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/lsp/index.test.ts +133 -0
- package/test/lsp/launch.test.ts +22 -0
- package/test/lsp/lifecycle.test.ts +147 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/mcp/lifecycle.test.ts +750 -0
- package/test/mcp/oauth-auto-connect.test.ts +199 -0
- package/test/mcp/oauth-browser.test.ts +249 -0
- package/test/mcp/sc-approve-validator.test.ts +431 -0
- package/test/memory/abort-leak.test.ts +137 -0
- package/test/npm.test.ts +18 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/permission/arity.test.ts +33 -0
- package/test/permission/next.test.ts +1123 -0
- package/test/permission-task.test.ts +323 -0
- package/test/plugin/auth-override.test.ts +74 -0
- package/test/plugin/codex.test.ts +123 -0
- package/test/plugin/github-copilot-models.test.ts +117 -0
- package/test/plugin/install-concurrency.test.ts +140 -0
- package/test/plugin/install.test.ts +570 -0
- package/test/plugin/loader-shared.test.ts +1136 -0
- package/test/plugin/meta.test.ts +137 -0
- package/test/plugin/shared.test.ts +88 -0
- package/test/plugin/trigger.test.ts +111 -0
- package/test/preload.ts +90 -0
- package/test/project/migrate-global.test.ts +140 -0
- package/test/project/project.test.ts +459 -0
- package/test/project/state.test.ts +115 -0
- package/test/project/vcs.test.ts +228 -0
- package/test/project/worktree-remove.test.ts +96 -0
- package/test/project/worktree.test.ts +173 -0
- package/test/provider/amazon-bedrock.test.ts +447 -0
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
- package/test/provider/error.test.ts +49 -0
- package/test/provider/gitlab-duo.test.ts +412 -0
- package/test/provider/provider.test.ts +2494 -0
- package/test/provider/transform.test.ts +2944 -0
- package/test/pty/pty-output-isolation.test.ts +141 -0
- package/test/pty/pty-session.test.ts +92 -0
- package/test/pty/pty-shell.test.ts +59 -0
- package/test/question/question.test.ts +453 -0
- package/test/server/global-session-list.test.ts +89 -0
- package/test/server/project-init-git.test.ts +121 -0
- package/test/server/session-actions.test.ts +83 -0
- package/test/server/session-list.test.ts +98 -0
- package/test/server/session-messages.test.ts +159 -0
- package/test/server/session-select.test.ts +84 -0
- package/test/session/compaction.test.ts +683 -0
- package/test/session/continuity-handover.test.ts +620 -0
- package/test/session/deterministic-handover.test.ts +328 -0
- package/test/session/doom-protection.test.ts +247 -0
- package/test/session/hard-reset.test.ts +179 -0
- package/test/session/instruction.test.ts +286 -0
- package/test/session/llm/monitor.test.ts +53 -0
- package/test/session/llm-sanitizer.test.ts +90 -0
- package/test/session/llm-zones-e2e.test.ts +61 -0
- package/test/session/llm.test.ts +1308 -0
- package/test/session/mcpx-normalization.test.ts +86 -0
- package/test/session/mcpx-syntax-recovery.test.ts +28 -0
- package/test/session/message-v2.test.ts +957 -0
- package/test/session/messages-pagination.test.ts +885 -0
- package/test/session/processor-effect.test.ts +805 -0
- package/test/session/prompt/builder.test.ts +71 -0
- package/test/session/prompt/engine-loop.test.ts +80 -0
- package/test/session/prompt/orchestrator.test.ts +108 -0
- package/test/session/prompt/resolver.test.ts +211 -0
- package/test/session/prompt/router.test.ts +84 -0
- package/test/session/prompt/state.test.ts +57 -0
- package/test/session/prompt-effect.test.ts +1241 -0
- package/test/session/prompt.test.ts +522 -0
- package/test/session/refactor-system-zones.test.ts +241 -0
- package/test/session/retry.test.ts +232 -0
- package/test/session/revert-compact.test.ts +621 -0
- package/test/session/sanitizer.test.ts +61 -0
- package/test/session/session.test.ts +142 -0
- package/test/session/snapshot-tool-race.test.ts +242 -0
- package/test/session/structured-output-integration.test.ts +233 -0
- package/test/session/structured-output.test.ts +391 -0
- package/test/session/system.test.ts +59 -0
- package/test/session/telemetry.test.ts +35 -0
- package/test/shell/shell.test.ts +73 -0
- package/test/skill/discovery.test.ts +116 -0
- package/test/skill/skill.test.ts +392 -0
- package/test/snapshot/snapshot.test.ts +1404 -0
- package/test/storage/db.test.ts +14 -0
- package/test/storage/json-migration.test.ts +791 -0
- package/test/storage/storage.test.ts +295 -0
- package/test/sync/index.test.ts +191 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +567 -0
- package/test/tool/bash.test.ts +1099 -0
- package/test/tool/edit.test.ts +681 -0
- package/test/tool/external-directory.test.ts +198 -0
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +65179 -0
- package/test/tool/grep.test.ts +111 -0
- package/test/tool/question.test.ts +126 -0
- package/test/tool/read.test.ts +468 -0
- package/test/tool/registry.test.ts +126 -0
- package/test/tool/skill.test.ts +167 -0
- package/test/tool/task.test.ts +49 -0
- package/test/tool/tool-define.test.ts +101 -0
- package/test/tool/truncation.test.ts +161 -0
- package/test/tool/webfetch.test.ts +101 -0
- package/test/tool/write.test.ts +354 -0
- package/test/util/data-url.test.ts +14 -0
- package/test/util/effect-zod.test.ts +61 -0
- package/test/util/error.test.ts +38 -0
- package/test/util/filesystem.test.ts +656 -0
- package/test/util/flock.test.ts +383 -0
- package/test/util/format.test.ts +59 -0
- package/test/util/glob.test.ts +164 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/lock.test.ts +72 -0
- package/test/util/log-parser.test.ts +61 -0
- package/test/util/module.test.ts +59 -0
- package/test/util/process.test.ts +128 -0
- package/test/util/telemetry-integration.test.ts +104 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/which.test.ts +100 -0
- package/test/util/wildcard.test.ts +90 -0
- package/test-regex.js +50 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { BusEvent } from "@/bus/bus-event"
|
|
2
|
+
import { Bus } from "@/bus"
|
|
3
|
+
import { makeRuntime } from "@/effect/run-service"
|
|
4
|
+
import { SessionID } from "./schema"
|
|
5
|
+
import { Effect, Layer, ServiceMap, Option } from "effect"
|
|
6
|
+
import z from "zod"
|
|
7
|
+
import { Database, eq, asc } from "../storage/db"
|
|
8
|
+
import { TodoTable } from "./session.sql"
|
|
9
|
+
import { AppFileSystem } from "@/filesystem"
|
|
10
|
+
import { Instance } from "../project/instance"
|
|
11
|
+
import * as path from "path"
|
|
12
|
+
|
|
13
|
+
export namespace Todo {
|
|
14
|
+
export const Info = z
|
|
15
|
+
.object({
|
|
16
|
+
content: z.string().describe("Brief description of the task"),
|
|
17
|
+
status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"),
|
|
18
|
+
priority: z.string().describe("Priority level of the task: high, medium, low"),
|
|
19
|
+
})
|
|
20
|
+
.meta({ ref: "Todo" })
|
|
21
|
+
export type Info = z.infer<typeof Info>
|
|
22
|
+
|
|
23
|
+
export const Event = {
|
|
24
|
+
Updated: BusEvent.define(
|
|
25
|
+
"todo.updated",
|
|
26
|
+
z.object({
|
|
27
|
+
sessionID: SessionID.zod,
|
|
28
|
+
todos: z.array(Info),
|
|
29
|
+
}),
|
|
30
|
+
),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Interface {
|
|
34
|
+
readonly update: (input: { sessionID: SessionID; todos: Info[] }) => Effect.Effect<void>
|
|
35
|
+
readonly get: (sessionID: SessionID) => Effect.Effect<Info[]>
|
|
36
|
+
readonly syncWithFile: (sessionID: SessionID) => Effect.Effect<void>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class Service extends ServiceMap.Service<Service, Interface>()("@epochcli/SessionTodo") {}
|
|
40
|
+
|
|
41
|
+
export const layer: Layer.Layer<Service, never, Bus.Service | AppFileSystem.Service> = Layer.effect(
|
|
42
|
+
Service,
|
|
43
|
+
Effect.gen(function* () {
|
|
44
|
+
const bus = yield* Bus.Service
|
|
45
|
+
const fs = yield* AppFileSystem.Service
|
|
46
|
+
|
|
47
|
+
const update = Effect.fn("Todo.update")(function* (input: { sessionID: SessionID; todos: Info[] }) {
|
|
48
|
+
yield* Effect.sync(() =>
|
|
49
|
+
Database.transaction((db) => {
|
|
50
|
+
db.delete(TodoTable).where(eq(TodoTable.session_id, input.sessionID)).run()
|
|
51
|
+
if (input.todos.length === 0) return
|
|
52
|
+
db.insert(TodoTable)
|
|
53
|
+
.values(
|
|
54
|
+
input.todos.map((todo, position) => ({
|
|
55
|
+
session_id: input.sessionID,
|
|
56
|
+
content: todo.content,
|
|
57
|
+
status: todo.status,
|
|
58
|
+
priority: todo.priority,
|
|
59
|
+
position,
|
|
60
|
+
})),
|
|
61
|
+
)
|
|
62
|
+
.run()
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
65
|
+
yield* bus.publish(Event.Updated, input)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const get = Effect.fn("Todo.get")(function* (sessionID: SessionID) {
|
|
69
|
+
const rows = yield* Effect.sync(() =>
|
|
70
|
+
Database.use((db) =>
|
|
71
|
+
db
|
|
72
|
+
.select()
|
|
73
|
+
.from(TodoTable)
|
|
74
|
+
.where(eq(TodoTable.session_id, sessionID))
|
|
75
|
+
.orderBy(asc(TodoTable.position))
|
|
76
|
+
.all(),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
return rows.map((row) => ({
|
|
80
|
+
content: row.content,
|
|
81
|
+
status: row.status,
|
|
82
|
+
priority: row.priority,
|
|
83
|
+
}))
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const syncWithFile = Effect.fn("Todo.syncWithFile")(function* (sessionID: SessionID) {
|
|
87
|
+
const root = Instance.directory
|
|
88
|
+
const projectsPath = path.join(root, "projects")
|
|
89
|
+
if (!(yield* fs.exists(projectsPath))) return
|
|
90
|
+
|
|
91
|
+
let latestFile: string | undefined
|
|
92
|
+
let latestTime = 0
|
|
93
|
+
|
|
94
|
+
const candidates = ["active", "completed", "closed"]
|
|
95
|
+
for (const cat of candidates) {
|
|
96
|
+
const catPath = path.join(projectsPath, cat)
|
|
97
|
+
const exists = yield* fs.exists(catPath)
|
|
98
|
+
if (!exists) continue
|
|
99
|
+
|
|
100
|
+
const projects = yield* fs.readDirectory(catPath)
|
|
101
|
+
for (const p of projects) {
|
|
102
|
+
const pPath = path.join(catPath, p)
|
|
103
|
+
const stats = yield* fs.stat(pPath)
|
|
104
|
+
const mtime = Option.getOrElse(stats.mtime, () => new Date(0)).getTime()
|
|
105
|
+
|
|
106
|
+
if (mtime > latestTime) {
|
|
107
|
+
const tasksFile = path.join(pPath, "Tasks.json")
|
|
108
|
+
const tasksFileLower = path.join(pPath, "tasks.json")
|
|
109
|
+
|
|
110
|
+
if (yield* fs.exists(tasksFile)) {
|
|
111
|
+
latestFile = tasksFile
|
|
112
|
+
latestTime = mtime
|
|
113
|
+
} else if (yield* fs.exists(tasksFileLower)) {
|
|
114
|
+
latestFile = tasksFileLower
|
|
115
|
+
latestTime = mtime
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!latestFile) return
|
|
122
|
+
|
|
123
|
+
const content = yield* fs.readFileString(latestFile)
|
|
124
|
+
const todos: Info[] = []
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const parsed = JSON.parse(content)
|
|
128
|
+
const taskList = Array.isArray(parsed) ? parsed : (Array.isArray(parsed.tasks) ? parsed.tasks : [])
|
|
129
|
+
|
|
130
|
+
for (const task of taskList) {
|
|
131
|
+
const title = task.title || task.description
|
|
132
|
+
if (title) {
|
|
133
|
+
const displayTitle = task.id ? `${task.id} ${title}` : title
|
|
134
|
+
todos.push({
|
|
135
|
+
content: displayTitle,
|
|
136
|
+
status: task.status || "pending",
|
|
137
|
+
priority: "medium",
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
// Ignore parse errors from partial writes or legacy markdown files
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
yield* update({ sessionID, todos })
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return Service.of({ update, get, syncWithFile: (id) => syncWithFile(id).pipe(Effect.ignore) })
|
|
149
|
+
}),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
export const defaultLayer = layer.pipe(Layer.provide(Bus.layer), Layer.provide(AppFileSystem.defaultLayer))
|
|
153
|
+
const { runPromise } = makeRuntime(Service, defaultLayer)
|
|
154
|
+
|
|
155
|
+
export async function update(input: { sessionID: SessionID; todos: Info[] }) {
|
|
156
|
+
return runPromise((svc) => svc.update(input))
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function get(sessionID: SessionID) {
|
|
160
|
+
return runPromise((svc) => svc.get(sessionID))
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function syncWithFile(sessionID: SessionID) {
|
|
164
|
+
return runPromise((svc) => svc.syncWithFile(sessionID))
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { Log } from "../util/log"
|
|
2
|
+
import { Effect } from "effect"
|
|
3
|
+
import { Provider } from "@/provider/provider"
|
|
4
|
+
import { generateText } from "@/util/ai-sdk"
|
|
5
|
+
import { MCP } from "@/mcp/index"
|
|
6
|
+
import { Config } from "@/config/config"
|
|
7
|
+
import fsNode from "fs/promises"
|
|
8
|
+
import { SessionAnalyzer } from "@/util/session-analyzer"
|
|
9
|
+
import { Instance } from "../project/instance"
|
|
10
|
+
import path from "path"
|
|
11
|
+
import { Glob } from "../util/glob"
|
|
12
|
+
|
|
13
|
+
const log = Log.create({ service: "post-generation-worker" })
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compacts chat history for the Clerk (Post-Generation Worker) to prevent context overflow.
|
|
17
|
+
* Strips large tool outputs and internal parts while preserving thoughts and metadata.
|
|
18
|
+
*/
|
|
19
|
+
function compactChatHistoryForClerk(history: any[]) {
|
|
20
|
+
return history.map((msg: any) => ({
|
|
21
|
+
...msg,
|
|
22
|
+
info: {
|
|
23
|
+
...(msg.info ?? {}),
|
|
24
|
+
role: msg.info?.role ?? msg.role,
|
|
25
|
+
agent: msg.info?.agent ?? msg.agent,
|
|
26
|
+
},
|
|
27
|
+
parts: (msg.parts ?? [])
|
|
28
|
+
.map((part: any) => {
|
|
29
|
+
if (part.type === "text") return { type: "text", text: part.text }
|
|
30
|
+
if (part.type === "reasoning") {
|
|
31
|
+
return {
|
|
32
|
+
type: "reasoning",
|
|
33
|
+
text:
|
|
34
|
+
part.text.length > 2000
|
|
35
|
+
? part.text.slice(0, 1000) + "\n... [Thinking Truncated] ...\n" + part.text.slice(-1000)
|
|
36
|
+
: part.text,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (part.type === "tool") {
|
|
40
|
+
const toolPart: any = {
|
|
41
|
+
type: "tool",
|
|
42
|
+
tool: part.tool,
|
|
43
|
+
input: part.state?.input ?? part.input,
|
|
44
|
+
}
|
|
45
|
+
const state = part.state ?? part
|
|
46
|
+
if (state.status === "completed" && state.output) {
|
|
47
|
+
const outStr = typeof state.output === "string" ? state.output : JSON.stringify(state.output)
|
|
48
|
+
if (outStr.length > 400) {
|
|
49
|
+
toolPart.output = `[Output Truncated: ${outStr.length} chars] ${outStr.slice(0, 200)}... [OMITTED] ...${outStr.slice(-200)}`
|
|
50
|
+
} else {
|
|
51
|
+
toolPart.output = state.output
|
|
52
|
+
}
|
|
53
|
+
} else if (state.status === "error") {
|
|
54
|
+
toolPart.error = state.error
|
|
55
|
+
}
|
|
56
|
+
return toolPart
|
|
57
|
+
}
|
|
58
|
+
return undefined
|
|
59
|
+
})
|
|
60
|
+
.filter(Boolean),
|
|
61
|
+
}))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export namespace PostGenerationWorker {
|
|
65
|
+
export const execute = Effect.fn("PostGenerationWorker.execute")(function* (input: {
|
|
66
|
+
sessionID: string
|
|
67
|
+
chatHistory: any[]
|
|
68
|
+
abortSignal: AbortSignal
|
|
69
|
+
isTransition?: boolean
|
|
70
|
+
isFinal?: boolean
|
|
71
|
+
}) {
|
|
72
|
+
// Give the primary model call a moment to finish its connection before Clerk starts
|
|
73
|
+
yield* Effect.sleep("1 seconds")
|
|
74
|
+
|
|
75
|
+
if (input.abortSignal.aborted) {
|
|
76
|
+
log.info("Post-generation task aborted before starting")
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const sideModel = yield* Effect.promise(() => Provider.getSideModel())
|
|
82
|
+
|
|
83
|
+
if (!sideModel) {
|
|
84
|
+
log.debug("No local-side model configured. Skipping post-generation tasks.")
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
log.info("Executing Phase 3: Post-Generation on local-side Clerk")
|
|
89
|
+
|
|
90
|
+
const sideLanguage = yield* Effect.promise(() => Provider.getLanguage(sideModel))
|
|
91
|
+
|
|
92
|
+
// Add a small artificial delay to prevent hitting local proxy rate limits
|
|
93
|
+
// when calls are made back-to-back sequentially
|
|
94
|
+
yield* Effect.sleep("1 seconds")
|
|
95
|
+
|
|
96
|
+
// Check cancellation token before calling LLM
|
|
97
|
+
if (input.abortSignal.aborted) return
|
|
98
|
+
|
|
99
|
+
// 1. Persistence Extraction (Semantic Merge into Global History Suite)
|
|
100
|
+
if (input.isTransition || input.isFinal) {
|
|
101
|
+
log.debug("Extracting and semantically merging architectural facts into Global History", { model: sideLanguage.modelId })
|
|
102
|
+
try {
|
|
103
|
+
const rulesPath = path.join(Instance.directory, ".history", "project_rules.toon")
|
|
104
|
+
|
|
105
|
+
// Ensure .history directory exists
|
|
106
|
+
yield* Effect.promise(() => fsNode.mkdir(path.dirname(rulesPath), { recursive: true }).catch(() => {}))
|
|
107
|
+
|
|
108
|
+
const existingRules = yield* Effect.promise(() => fsNode.readFile(rulesPath, "utf-8").catch(() => "NONE"))
|
|
109
|
+
|
|
110
|
+
const extractionRes = yield* Effect.promise(() =>
|
|
111
|
+
generateText({
|
|
112
|
+
model: sideLanguage,
|
|
113
|
+
system:
|
|
114
|
+
'You are an expert architectural fact extractor and deduplicator.\n' +
|
|
115
|
+
'Your task is to analyze the provided chat history for any NEW, universally applicable architectural rules, stylistic constraints, or user preferences established during this epoch.\n' +
|
|
116
|
+
'You must compare these new findings against the EXISTING RULES provided below.\n\n' +
|
|
117
|
+
'CRITICAL INSTRUCTIONS:\n' +
|
|
118
|
+
'1. Identify any truly net-new rules from the Chat History.\n' +
|
|
119
|
+
'2. Semantically merge them with the EXISTING RULES. Do NOT duplicate rules that mean the same thing.\n' +
|
|
120
|
+
'3. Output the COMPLETE, UPDATED, and DEDUPLICATED list of rules.\n' +
|
|
121
|
+
'4. If no rules exist at all, output \'NONE\'.\n' +
|
|
122
|
+
'5. You MUST output ONLY valid TOON format exactly like this:\n' +
|
|
123
|
+
'rules[fact_id, trigger, behaviour]:\n' +
|
|
124
|
+
' fact_01, "When [condition]", "[behavior]"\n' +
|
|
125
|
+
' fact_02, "When [condition]", "[behavior]"\n',
|
|
126
|
+
prompt: `=== EXISTING RULES ===\n${existingRules}\n\n=== CHAT HISTORY (Current Epoch) ===\n${JSON.stringify(compactChatHistoryForClerk(input.chatHistory))}`,
|
|
127
|
+
abortSignal: input.abortSignal,
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
log.debug("Persistence extraction and merge complete", { text: extractionRes.text.slice(0, 100) })
|
|
132
|
+
|
|
133
|
+
if (input.abortSignal.aborted) return
|
|
134
|
+
|
|
135
|
+
const newContent = extractionRes.text.trim()
|
|
136
|
+
if (newContent !== "NONE" && newContent.length > 0) {
|
|
137
|
+
log.info("Facts extracted and merged, writing to .history/project_rules.toon")
|
|
138
|
+
yield* Effect.promise(() => fsNode.writeFile(rulesPath, newContent))
|
|
139
|
+
log.debug(`Wrote compacted facts to ${rulesPath}`)
|
|
140
|
+
}
|
|
141
|
+
} catch (e) {
|
|
142
|
+
log.error("Persistence extraction and merge failed", { error: String(e), model: sideLanguage.modelId })
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
log.debug("Skipping Zone 2 extraction: Not an epoch transition or final turn.")
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (input.abortSignal.aborted) return
|
|
149
|
+
|
|
150
|
+
// 2. Generate Epoch Continuity Report
|
|
151
|
+
log.debug("Generating Epoch Continuity Report", { model: sideLanguage.modelId })
|
|
152
|
+
const analysis = yield* Effect.promise(() => SessionAnalyzer.analyze(input.sessionID, compactChatHistoryForClerk(input.chatHistory), Instance.directory))
|
|
153
|
+
|
|
154
|
+
// Fetch Project Map status if available
|
|
155
|
+
let mapStatus = "NOT_AVAILABLE"
|
|
156
|
+
try {
|
|
157
|
+
const mcpClientsRecord = yield* Effect.promise(() => MCP.clients())
|
|
158
|
+
const mapCli = mcpClientsRecord["map"] as any
|
|
159
|
+
if (mapCli) {
|
|
160
|
+
const res = yield* Effect.promise(() => mapCli.callTool({ name: "pm_status", arguments: {} }))
|
|
161
|
+
mapStatus = (res as any).output || "HEALTHY"
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
log.debug("Failed to fetch map status for continuity report", { error: String(e) })
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 2.1 Automated Project Context Discovery
|
|
168
|
+
let activeProjectContext = "NONE_FOUND"
|
|
169
|
+
try {
|
|
170
|
+
const activeDir = path.join(Instance.directory, "projects", "active")
|
|
171
|
+
const stats = yield* Effect.promise(() => fsNode.stat(activeDir).catch(() => null))
|
|
172
|
+
if (stats?.isDirectory()) {
|
|
173
|
+
const entries = yield* Effect.promise(() => fsNode.readdir(activeDir))
|
|
174
|
+
if (entries.length > 0) {
|
|
175
|
+
const entryStats = yield* Effect.promise(() =>
|
|
176
|
+
Promise.all(
|
|
177
|
+
entries.map(async (e) => ({
|
|
178
|
+
name: e,
|
|
179
|
+
stat: await fsNode.stat(path.join(activeDir, e)),
|
|
180
|
+
})),
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
const latest = entryStats
|
|
184
|
+
.filter((e) => e.stat.isDirectory())
|
|
185
|
+
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)[0]
|
|
186
|
+
|
|
187
|
+
if (latest) {
|
|
188
|
+
activeProjectContext = `FEATURE_ID: ${latest.name} (Last Modified: ${new Date(latest.stat.mtimeMs).toISOString()})`
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const lastUsedPath = path.join(Instance.directory, ".spec_last_used")
|
|
194
|
+
const lastUsed = yield* Effect.promise(() => fsNode.readFile(lastUsedPath, "utf-8").catch(() => null))
|
|
195
|
+
if (lastUsed) {
|
|
196
|
+
activeProjectContext += `\nSPEC_LAST_USED: ${lastUsed.trim()}`
|
|
197
|
+
}
|
|
198
|
+
} catch (e) {
|
|
199
|
+
log.debug("Context discovery failed", { error: String(e) })
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const agentsPath = path.join(Instance.directory, "AGENTS.md")
|
|
203
|
+
const agentsContent = yield* Effect.promise(() => fsNode.readFile(agentsPath, "utf-8").catch(() => ""))
|
|
204
|
+
|
|
205
|
+
const analysisPromptRaw = [
|
|
206
|
+
"You are an expert technical supervisor generating an Epoch Continuity Report.",
|
|
207
|
+
"This report is a succinct executive summary of the project state. Technical specificity is paramount, but brevity is required for the executive summary sections.",
|
|
208
|
+
"You MUST output the report in strict TOON (Token-Oriented Object Notation) format.",
|
|
209
|
+
"Do NOT use markdown formatting. Use YAML-like indentation with clear key-value pairs.",
|
|
210
|
+
"Structure your output exactly like this:",
|
|
211
|
+
"epoch_continuity:",
|
|
212
|
+
" historical_references:",
|
|
213
|
+
" - topic: 'Discarded Approaches & Deep Context'",
|
|
214
|
+
" file: '.history/intent.toon'",
|
|
215
|
+
" trigger: 'Read this before starting a new complex implementation to avoid repeating mistakes.'",
|
|
216
|
+
" - topic: 'Full Tool History & Output Logs'",
|
|
217
|
+
" file: '.history/timeline.toon'",
|
|
218
|
+
" trigger: 'Read this if you need to see exactly what commands were run and their raw output.'",
|
|
219
|
+
" - topic: 'Detailed Error Logs'",
|
|
220
|
+
" file: '.history/errors.toon'",
|
|
221
|
+
" trigger: 'Read this if you are trying to fix a persistent bug that spanned epochs.'",
|
|
222
|
+
" - topic: 'Registry of Touched Files'",
|
|
223
|
+
" file: '.history/files.toon'",
|
|
224
|
+
" trigger: 'Read this if you need to know which files were modified in previous epochs.'",
|
|
225
|
+
input.isTransition
|
|
226
|
+
? " - topic: 'Immediate Working Memory & Interrupted Thoughts'\n file: '.history/interrupted_state.toon'\n trigger: 'READ THIS FIRST. You were interrupted by a context limit right before executing a tool. This contains your exact mental drafts and intentions.'"
|
|
227
|
+
: "",
|
|
228
|
+
" workflow_map:",
|
|
229
|
+
" pipeline_step: 'Step (1-5)'",
|
|
230
|
+
" phase: 'Current Spec Phase (e.g., Requirements, Design, Build)'",
|
|
231
|
+
" status: 'Ready / Blocked / In-Progress'",
|
|
232
|
+
" project_map:",
|
|
233
|
+
" status: 'Healthy/Stale/Uninitialized'",
|
|
234
|
+
" context: 'Brief summary of what the map currently tracks (e.g., \"8 symbols in core/\")'",
|
|
235
|
+
" executive_intent:",
|
|
236
|
+
" high_level_architecture: 'The overarching mental model or design pattern the agent was constructing.'",
|
|
237
|
+
" immediate_conclusions: ['Final takeaways that guide the next action']",
|
|
238
|
+
" technical_progress:",
|
|
239
|
+
" completed_artifacts: ['files finalized with brief details on added symbols']",
|
|
240
|
+
" blocked_drafts: ['files requiring attention, with a brief status of why they are blocked']",
|
|
241
|
+
" function_activity: 'Literal trace of the last 2-3 tool calls'",
|
|
242
|
+
" next_action:",
|
|
243
|
+
" tool: 'tool_name'",
|
|
244
|
+
" rationale: 'Deep technical reasoning for why this tool is next'",
|
|
245
|
+
" example_input: { ... } # A valid, complete JSON object for the next agent",
|
|
246
|
+
"\ninterrupted_state:",
|
|
247
|
+
" context_of_interruption: 'Brief description of what you were doing when the turn or epoch ended.'",
|
|
248
|
+
" thought_tail: 'The last few sentences of your internal reasoning/thoughts right before the transition.'",
|
|
249
|
+
" partial_tool_call_fragment: 'If you were in the middle of typing a large tool call (JSON), extract the recovered buffer here.'",
|
|
250
|
+
" last_mental_drafts: 'Dense, verbose extraction or summary of the exact document content or code you were formulating in your reasoning channel just before interruption. DO NOT INVENT CONTENT; extract it from the thoughts.'",
|
|
251
|
+
" resumption_directive: 'A concrete, stern, one-sentence command telling the next agent exactly what to do first to resume progress.'",
|
|
252
|
+
"\nINSTRUCTIONS:",
|
|
253
|
+
"1. Analyze the ACTION TIMELINE and RECENT AGENT THOUGHTS below.",
|
|
254
|
+
"2. ARCHITECTURAL PRESERVATION: You MUST explicitly recap high-level design patterns and implementation strategies established earlier in the epoch in the `executive_intent` section.",
|
|
255
|
+
"3. DENSITY MANDATE: Provide code-level architectural context in `technical_progress`. Do NOT use vague summaries.",
|
|
256
|
+
"4. LENGTH GOAL: Aim for a dense report of approximately 600-800 tokens. DO NOT EXCEED 1000 tokens.",
|
|
257
|
+
"5. If a tool like 'sc_plan' returns 'Please finish editing...', the 'phase' is 'Specification', 'status' is 'Blocked', and the file should be listed in 'blocked_drafts'.",
|
|
258
|
+
"6. PROJECT MAP ORIENTATION: If the ACTION TIMELINE shows repetitive exploratory loops (ls, read, grep) for orientation, recommend using `map pm_query` or `map pm_plan` in the next_action rationale. Use the provided PROJECT_MAP_STATUS to judge map accuracy.",
|
|
259
|
+
"7. You MUST provide a concrete 'example_input' for the 'next_action'. If the next action is to fix a file, provide the 'edit' or 'replace' parameters. For 'mcpx' or 'bash' commands, you may omit 'example_input' as the main agent will use AGENTS.md for syntax.",
|
|
260
|
+
"8. EXECUTIVE INTENT: Deeply analyze the provided RECENT AGENT THOUGHTS. Distill the agent's internal monologue into the structured `executive_intent` fields. Offload verbose reasoning to the `.history` files via the signposts.",
|
|
261
|
+
"9. INTERRUPTED STATE EXTRACTION: You MUST focus on the VERY LAST reasoning blocks. Extract the literal content, code, or partial tool calls into `interrupted_state` fields. This acts as a hot-swap restore point for the next epoch.",
|
|
262
|
+
"10. PIPELINE TRACKING: You MUST identify the current step (1-5) in the 'Documentation-to-Build' pipeline based on the ACTION TIMELINE and SPEC status. Record this in `workflow_map.pipeline_step`.",
|
|
263
|
+
"11. DENSE ACTIVITY ONLY: Only provide a dense, 2-3 line chronological summary of the most critical actions in the `function_activity` field.",
|
|
264
|
+
"12. PROJECT DISCOVERY: Use the provided ACTIVE PROJECT CONTEXT to determine the current feature and phase. If FEATURE_ID is present, the project is NOT Uninitialized.",
|
|
265
|
+
"13. ARTIFACT VERIFICATION (CRITICAL): Use the VERIFIED_FILES list below as the source of truth for `completed_artifacts`. If a file is in MISSING_ARTIFACTS, it MUST NOT be listed as completed; if it was intended to be created, list it in `blocked_drafts` instead.",
|
|
266
|
+
agentsContent ? `\nPROJECT GUIDELINES (AGENTS.md):\n${agentsContent}` : "",
|
|
267
|
+
`\nPROJECT_MAP_STATUS:\n${mapStatus}`,
|
|
268
|
+
`\nACTIVE PROJECT CONTEXT (Ground Truth):\n${activeProjectContext}`,
|
|
269
|
+
`\nVERIFIED_FILES (Ground Truth - Files currently on disk):\n${analysis.verifiedFiles?.join("\n") || "None"}`,
|
|
270
|
+
`\nMISSING_ARTIFACTS (Files referenced in tools but NOT on disk):\n${analysis.missingFiles?.join("\n") || "None"}`,
|
|
271
|
+
"\nRECENT AGENT THOUGHTS:\n" +
|
|
272
|
+
(analysis.thoughts && analysis.thoughts.length > 0 ? analysis.thoughts.join("\n\n---\n\n") : "None"),
|
|
273
|
+
analysis.interruptedToolCall ? `\nINTERRUPTED TOOL CALL FRAGMENT (${analysis.interruptedToolCall.tool}):\n${analysis.interruptedToolCall.raw}\n` : "",
|
|
274
|
+
"\nACTION TIMELINE (for your analysis only, do not copy verbatim):\n" +
|
|
275
|
+
analysis.actionTimeline +
|
|
276
|
+
"\n\nTECHNICAL TELEMETRY:\n" +
|
|
277
|
+
JSON.stringify(analysis.telemetry, null, 2),
|
|
278
|
+
]
|
|
279
|
+
.filter(Boolean)
|
|
280
|
+
.join("\n")
|
|
281
|
+
|
|
282
|
+
// Hard context defense: Limit prompt to 90% of model's context window
|
|
283
|
+
// 1.4 chars per token is a realistic heuristic for structured TOON/JSON payloads on local BPE models
|
|
284
|
+
const charLimit = sideModel.limit.context * 0.9 * 1.4
|
|
285
|
+
let analysisPrompt = analysisPromptRaw
|
|
286
|
+
if (analysisPromptRaw.length > charLimit) {
|
|
287
|
+
log.error("SUPERVISOR_CONTEXT_CUTOFF_TRIGGERED", {
|
|
288
|
+
actualLength: analysisPromptRaw.length,
|
|
289
|
+
limit: charLimit,
|
|
290
|
+
sessionID: input.sessionID,
|
|
291
|
+
})
|
|
292
|
+
analysisPrompt =
|
|
293
|
+
analysisPromptRaw.slice(0, charLimit) + "\n\n[ERROR: PROMPT TRUNCATED DUE TO CONTEXT DEFENSE LIMIT]"
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let continuityRes
|
|
297
|
+
try {
|
|
298
|
+
log.debug("Continuity report prompt", { prompt: analysisPrompt })
|
|
299
|
+
continuityRes = yield* Effect.promise(() =>
|
|
300
|
+
generateText({
|
|
301
|
+
model: sideLanguage,
|
|
302
|
+
system:
|
|
303
|
+
"You are generating a dense, technical continuation state document. Output ONLY strict TOON format without markdown code blocks.\nLOOP MITIGATION: If you detect repetitive exploratory tool calls (read, ls, grep, which) in the action timeline without progress, you MUST recommend a decisive next_action using 'edit' or 'write' to resolve the underlying blocker.",
|
|
304
|
+
prompt: analysisPrompt,
|
|
305
|
+
abortSignal: input.abortSignal,
|
|
306
|
+
}),
|
|
307
|
+
)
|
|
308
|
+
log.debug("Continuity report generated", { length: continuityRes.text.length })
|
|
309
|
+
} catch (e) {
|
|
310
|
+
log.error("Continuity report generation failed", { error: String(e), model: sideLanguage.modelId })
|
|
311
|
+
throw e
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (input.abortSignal.aborted) return
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
let finalContent = continuityRes.text
|
|
318
|
+
.replace(/^```toon\n/, "")
|
|
319
|
+
.replace(/^```\n/, "")
|
|
320
|
+
.replace(/```$/, "")
|
|
321
|
+
.trim()
|
|
322
|
+
|
|
323
|
+
const continuityMatch = finalContent.match(/epoch_continuity:\n([\s\S]+?)(?=\ninterrupted_state:|$)/)
|
|
324
|
+
const interruptedMatch = finalContent.match(/interrupted_state:\n([\s\S]+?)$/)
|
|
325
|
+
|
|
326
|
+
if (continuityMatch) {
|
|
327
|
+
const continuityPath = path.join(Instance.directory, ".epoch-continuity.toon")
|
|
328
|
+
yield* Effect.promise(() => fsNode.writeFile(continuityPath, continuityMatch[0].trim()))
|
|
329
|
+
log.info(`Wrote .epoch-continuity.toon to ${continuityPath}`)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (interruptedMatch) {
|
|
333
|
+
const historyDir = path.join(Instance.directory, ".history")
|
|
334
|
+
yield* Effect.promise(() => fsNode.mkdir(historyDir, { recursive: true }).catch(() => {}))
|
|
335
|
+
const interruptedPath = path.join(historyDir, "interrupted_state.toon")
|
|
336
|
+
yield* Effect.promise(() => fsNode.writeFile(interruptedPath, interruptedMatch[0].trim()))
|
|
337
|
+
log.info(`Wrote interrupted state to ${interruptedPath}`)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Update global history suite (.history/*.toon)
|
|
341
|
+
yield* Effect.promise(() => SessionAnalyzer.exportHistoryToToon(Instance.directory))
|
|
342
|
+
} catch (e) {
|
|
343
|
+
log.warn("Failed to write .epoch-continuity.toon or history suite", { error: String(e) })
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// 3. Epoch Summarization & Spec Advance (ONLY if transition or final)
|
|
347
|
+
if (input.isTransition || input.isFinal) {
|
|
348
|
+
const mcpClientsRecord = yield* Effect.promise(() => MCP.clients())
|
|
349
|
+
const mcpClients = Object.values(mcpClientsRecord) as any[]
|
|
350
|
+
const specCli = mcpClients.find((c) => c.id === "spec")
|
|
351
|
+
|
|
352
|
+
if (specCli && !input.abortSignal.aborted) {
|
|
353
|
+
log.debug("Advancing task state via spec")
|
|
354
|
+
try {
|
|
355
|
+
const listRes = yield* Effect.promise(() => specCli.callTool({ name: "sc_todo_list", arguments: {} }))
|
|
356
|
+
const content = (listRes as any).content as any[]
|
|
357
|
+
if (content && content.length > 0 && content[0].type === "text") {
|
|
358
|
+
const activeTaskMatch = content[0].text.match(/\[[-~]\]\s+(\d+\.\d+)/)
|
|
359
|
+
if (activeTaskMatch) {
|
|
360
|
+
const taskId = activeTaskMatch[1]
|
|
361
|
+
log.info(`Autonomously marking task ${taskId} as complete`)
|
|
362
|
+
yield* Effect.promise(() =>
|
|
363
|
+
specCli.client.callTool({ name: "sc_todo_complete", arguments: { id: taskId } }),
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
} catch (e) {
|
|
368
|
+
log.warn("Failed autonomous spec-cli update", { error: String(e) })
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
log.info("Phase 3 Post-Generation complete")
|
|
374
|
+
} catch (error: any) {
|
|
375
|
+
if (error.name === "AbortError") {
|
|
376
|
+
log.info("Phase 3 aborted due to new user input")
|
|
377
|
+
} else {
|
|
378
|
+
log.error("Error in Post-Generation Worker", { error: String(error) })
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Flag } from "@/flag/flag"
|
|
2
|
+
import { lazy } from "@/util/lazy"
|
|
3
|
+
import { Filesystem } from "@/util/filesystem"
|
|
4
|
+
import { which } from "@/util/which"
|
|
5
|
+
import path from "path"
|
|
6
|
+
import { spawn, type ChildProcess } from "child_process"
|
|
7
|
+
import { setTimeout as sleep } from "node:timers/promises"
|
|
8
|
+
|
|
9
|
+
const SIGKILL_TIMEOUT_MS = 200
|
|
10
|
+
|
|
11
|
+
export namespace Shell {
|
|
12
|
+
const BLACKLIST = new Set(["fish", "nu"])
|
|
13
|
+
const LOGIN = new Set(["bash", "dash", "fish", "ksh", "sh", "zsh"])
|
|
14
|
+
const POSIX = new Set(["bash", "dash", "ksh", "sh", "zsh"])
|
|
15
|
+
|
|
16
|
+
export async function killTree(proc: ChildProcess, opts?: { exited?: () => boolean }): Promise<void> {
|
|
17
|
+
const pid = proc.pid
|
|
18
|
+
if (!pid || opts?.exited?.()) return
|
|
19
|
+
|
|
20
|
+
if (process.platform === "win32") {
|
|
21
|
+
await new Promise<void>((resolve) => {
|
|
22
|
+
const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
|
|
23
|
+
stdio: "ignore",
|
|
24
|
+
windowsHide: true,
|
|
25
|
+
})
|
|
26
|
+
killer.once("exit", () => resolve())
|
|
27
|
+
killer.once("error", () => resolve())
|
|
28
|
+
})
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
process.kill(-pid, "SIGTERM")
|
|
34
|
+
await sleep(SIGKILL_TIMEOUT_MS)
|
|
35
|
+
if (!opts?.exited?.()) {
|
|
36
|
+
process.kill(-pid, "SIGKILL")
|
|
37
|
+
}
|
|
38
|
+
} catch (_e) {
|
|
39
|
+
proc.kill("SIGTERM")
|
|
40
|
+
await sleep(SIGKILL_TIMEOUT_MS)
|
|
41
|
+
if (!opts?.exited?.()) {
|
|
42
|
+
proc.kill("SIGKILL")
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function full(file: string) {
|
|
48
|
+
if (process.platform !== "win32") return file
|
|
49
|
+
const shell = Filesystem.windowsPath(file)
|
|
50
|
+
if (path.win32.dirname(shell) !== ".") {
|
|
51
|
+
if (shell.startsWith("/") && name(shell) === "bash") return gitbash() || shell
|
|
52
|
+
return shell
|
|
53
|
+
}
|
|
54
|
+
return Bun.which(shell) || shell
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function pick() {
|
|
58
|
+
const pwsh = Bun.which("pwsh")
|
|
59
|
+
if (pwsh) return pwsh
|
|
60
|
+
const powershell = Bun.which("powershell")
|
|
61
|
+
if (powershell) return powershell
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function select(file: string | undefined, opts?: { acceptable?: boolean }) {
|
|
65
|
+
if (file && (!opts?.acceptable || !BLACKLIST.has(name(file)))) return full(file)
|
|
66
|
+
if (process.platform === "win32") {
|
|
67
|
+
const shell = pick()
|
|
68
|
+
if (shell) return shell
|
|
69
|
+
}
|
|
70
|
+
return fallback()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function gitbash() {
|
|
74
|
+
if (process.platform !== "win32") return
|
|
75
|
+
if (Flag.EPOCHCLI_GIT_BASH_PATH) return Flag.EPOCHCLI_GIT_BASH_PATH
|
|
76
|
+
const git = which("git")
|
|
77
|
+
if (!git) return
|
|
78
|
+
const file = path.join(git, "..", "..", "bin", "bash.exe")
|
|
79
|
+
if (Filesystem.stat(file)?.size) return file
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function fallback() {
|
|
83
|
+
if (process.platform === "win32") {
|
|
84
|
+
const file = gitbash()
|
|
85
|
+
if (file) return file
|
|
86
|
+
return process.env.COMSPEC || "cmd.exe"
|
|
87
|
+
}
|
|
88
|
+
if (process.platform === "darwin") return "/bin/zsh"
|
|
89
|
+
const bash = which("bash")
|
|
90
|
+
if (bash) return bash
|
|
91
|
+
return "/bin/sh"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function name(file: string) {
|
|
95
|
+
if (process.platform === "win32") return path.win32.parse(Filesystem.windowsPath(file)).name.toLowerCase()
|
|
96
|
+
return path.basename(file).toLowerCase()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function login(file: string) {
|
|
100
|
+
return LOGIN.has(name(file))
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function posix(file: string) {
|
|
104
|
+
return POSIX.has(name(file))
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const preferred = lazy(() => select(process.env.SHELL))
|
|
108
|
+
|
|
109
|
+
export const acceptable = lazy(() => select(process.env.SHELL, { acceptable: true }))
|
|
110
|
+
}
|