@ashx-j/lunr 0.2.26 → 0.2.27
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/CHANGELOG.md +16 -1
- package/LICENSE +22 -0
- package/dist/builtin-extensions/ashxj-thinking.d.ts +1 -1
- package/dist/builtin-extensions/ashxj-thinking.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js +1 -1
- package/dist/builtin-extensions/ashxj-thinking.js.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts +11 -1
- package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
- package/dist/builtin-extensions/ashxj-tui.js +6 -10
- package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
- package/dist/builtin-extensions/lunr-cron.d.ts +4 -5
- package/dist/builtin-extensions/lunr-cron.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-cron.js +94 -108
- package/dist/builtin-extensions/lunr-cron.js.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.d.ts.map +1 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js +3 -1
- package/dist/builtin-extensions/lunr-local-providers/local-servers.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js +2 -2
- package/dist/builtin-extensions/narumiruna-pi-goal/src/commands.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/goal.js.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.d.ts.map +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js +1 -1
- package/dist/builtin-extensions/narumiruna-pi-goal/src/runtime.js.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-intercom/index.js +2 -2
- package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/bemol-extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js +1 -15
- package/dist/builtin-extensions/pi-lsp-extension/src/index.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts +6 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js +187 -132
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-client.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts +8 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js +142 -112
- package/dist/builtin-extensions/pi-lsp-extension/src/lsp-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts +12 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js +14 -7
- package/dist/builtin-extensions/pi-lsp-extension/src/tools/code-rewrite.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts +11 -3
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js +67 -41
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/rewrite-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts +4 -2
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js +3 -0
- package/dist/builtin-extensions/pi-lsp-extension/src/tree-sitter/search-engine.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts +2 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js +6 -6
- package/dist/builtin-extensions/pi-mcp-adapter/commands.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/config.js +11 -5
- package/dist/builtin-extensions/pi-mcp-adapter/config.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/direct-tool-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/host-html-template.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/index.js +48 -40
- package/dist/builtin-extensions/pi-mcp-adapter/index.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js +3 -1
- package/dist/builtin-extensions/pi-mcp-adapter/init.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts +12 -40
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js +166 -235
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-auth-flow.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts +5 -0
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js +43 -13
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-callback-server.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts +14 -3
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js +73 -21
- package/dist/builtin-extensions/pi-mcp-adapter/mcp-oauth-provider.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js +4 -4
- package/dist/builtin-extensions/pi-mcp-adapter/proxy-modes.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js +2 -2
- package/dist/builtin-extensions/pi-mcp-adapter/server-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts +2 -0
- package/dist/builtin-extensions/pi-mcp-adapter/state.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-mcp-adapter/state.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/index.js.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts +11 -5
- package/dist/builtin-extensions/pi-ollama-cloud/models.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-ollama-cloud/models.js +12 -32
- package/dist/builtin-extensions/pi-ollama-cloud/models.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/index.js +5 -3
- package/dist/builtin-extensions/pi-prompt-template-model/index.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts +2 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js +21 -12
- package/dist/builtin-extensions/pi-prompt-template-model/prompt-loader.js.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js +7 -24
- package/dist/builtin-extensions/pi-prompt-template-model/tool-manager.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js +39 -77
- package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +15 -31
- package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +7 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +10 -2
- package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js +8 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/chain-execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +106 -24
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +25 -5
- package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +7 -8
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js +60 -32
- package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js +15 -5
- package/dist/builtin-extensions/pi-subagents/src/shared/utils.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +4 -3
- package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js +17 -8
- package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js +3 -3
- package/dist/builtin-extensions/pi-subagents/src/watchdog/review.js.map +1 -1
- package/dist/builtin-extensions/pi-tps.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-tps.js +20 -17
- package/dist/builtin-extensions/pi-tps.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.d.ts +1 -0
- package/dist/builtin-extensions/pi-web-access/activity.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/activity.js +27 -5
- package/dist/builtin-extensions/pi-web-access/activity.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts +3 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js +43 -0
- package/dist/builtin-extensions/pi-web-access/bounded-body.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts +24 -0
- package/dist/builtin-extensions/pi-web-access/content-page.d.ts.map +1 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js +57 -0
- package/dist/builtin-extensions/pi-web-access/content-page.js.map +1 -0
- package/dist/builtin-extensions/pi-web-access/extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/extract.js +7 -2
- package/dist/builtin-extensions/pi-web-access/extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts +0 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/github-extract.js +147 -36
- package/dist/builtin-extensions/pi-web-access/github-extract.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/index.js +48 -73
- package/dist/builtin-extensions/pi-web-access/index.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts +12 -4
- package/dist/builtin-extensions/pi-web-access/session-cleanup.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js +35 -12
- package/dist/builtin-extensions/pi-web-access/session-cleanup.js.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.d.ts +9 -6
- package/dist/builtin-extensions/pi-web-access/storage.d.ts.map +1 -1
- package/dist/builtin-extensions/pi-web-access/storage.js +36 -24
- package/dist/builtin-extensions/pi-web-access/storage.js.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts +1 -1
- package/dist/builtin-extensions/simple-pi-memory.d.ts.map +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js +1 -1
- package/dist/builtin-extensions/simple-pi-memory.js.map +1 -1
- package/dist/cli/install-cli.d.ts.map +1 -1
- package/dist/cli/install-cli.js +38 -30
- package/dist/cli/install-cli.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts +13 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +75 -11
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +1 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +40 -6
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +309 -76
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/browser/runtime.d.ts +2 -0
- package/dist/core/browser/runtime.d.ts.map +1 -1
- package/dist/core/browser/runtime.js +80 -45
- package/dist/core/browser/runtime.js.map +1 -1
- package/dist/core/browser/setup.d.ts.map +1 -1
- package/dist/core/browser/setup.js +12 -1
- package/dist/core/browser/setup.js.map +1 -1
- package/dist/core/computer-use/adapter.d.ts.map +1 -1
- package/dist/core/computer-use/adapter.js +7 -2
- package/dist/core/computer-use/adapter.js.map +1 -1
- package/dist/core/computer-use/image.d.ts.map +1 -1
- package/dist/core/computer-use/image.js +17 -5
- package/dist/core/computer-use/image.js.map +1 -1
- package/dist/core/computer-use/schemas.d.ts.map +1 -1
- package/dist/core/computer-use/schemas.js +23 -6
- package/dist/core/computer-use/schemas.js.map +1 -1
- package/dist/core/computer-use/workflow.d.ts.map +1 -1
- package/dist/core/computer-use/workflow.js +459 -119
- package/dist/core/computer-use/workflow.js.map +1 -1
- package/dist/core/cron/jobs.d.ts +26 -8
- package/dist/core/cron/jobs.d.ts.map +1 -1
- package/dist/core/cron/jobs.js +357 -232
- package/dist/core/cron/jobs.js.map +1 -1
- package/dist/core/cron/origin-context.d.ts +1 -0
- package/dist/core/cron/origin-context.d.ts.map +1 -1
- package/dist/core/cron/origin-context.js.map +1 -1
- package/dist/core/cron/scheduler.d.ts +22 -17
- package/dist/core/cron/scheduler.d.ts.map +1 -1
- package/dist/core/cron/scheduler.js +153 -73
- package/dist/core/cron/scheduler.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +8 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +8 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +4 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +22 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/large-subagent-launch.d.ts +1 -1
- package/dist/core/large-subagent-launch.d.ts.map +1 -1
- package/dist/core/large-subagent-launch.js +7 -7
- package/dist/core/large-subagent-launch.js.map +1 -1
- package/dist/core/model-registry.d.ts +2 -0
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +5 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-runtime.d.ts +4 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +40 -4
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions.d.ts +3 -3
- package/dist/core/permissions.d.ts.map +1 -1
- package/dist/core/permissions.js +11 -35
- package/dist/core/permissions.js.map +1 -1
- package/dist/core/plan-mode.d.ts +5 -13
- package/dist/core/plan-mode.d.ts.map +1 -1
- package/dist/core/plan-mode.js +126 -116
- package/dist/core/plan-mode.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/prompt-resource-bridge.d.ts +12 -0
- package/dist/core/prompt-resource-bridge.d.ts.map +1 -0
- package/dist/core/prompt-resource-bridge.js +9 -0
- package/dist/core/prompt-resource-bridge.js.map +1 -0
- package/dist/core/protected-targets.d.ts +6 -0
- package/dist/core/protected-targets.d.ts.map +1 -0
- package/dist/core/protected-targets.js +45 -0
- package/dist/core/protected-targets.js.map +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +7 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/rollback.d.ts +19 -3
- package/dist/core/rollback.d.ts.map +1 -1
- package/dist/core/rollback.js +172 -56
- package/dist/core/rollback.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +3 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +14 -1
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts +3 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +6 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +95 -32
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-ownership.d.ts.map +1 -1
- package/dist/core/session-ownership.js +8 -3
- package/dist/core/session-ownership.js.map +1 -1
- package/dist/core/session-retention.d.ts.map +1 -1
- package/dist/core/session-retention.js +18 -7
- package/dist/core/session-retention.js.map +1 -1
- package/dist/core/session-startup-settings.d.ts +10 -0
- package/dist/core/session-startup-settings.d.ts.map +1 -0
- package/dist/core/session-startup-settings.js +70 -0
- package/dist/core/session-startup-settings.js.map +1 -0
- package/dist/core/subscriptions.d.ts +16 -7
- package/dist/core/subscriptions.d.ts.map +1 -1
- package/dist/core/subscriptions.js +96 -55
- package/dist/core/subscriptions.js.map +1 -1
- package/dist/core/trust-manager.d.ts +2 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +6 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/update-check.d.ts.map +1 -1
- package/dist/core/update-check.js.map +1 -1
- package/dist/core/usage-adapters/kimi-coding.d.ts +2 -2
- package/dist/core/usage-adapters/kimi-coding.d.ts.map +1 -1
- package/dist/core/usage-adapters/kimi-coding.js +2 -2
- package/dist/core/usage-adapters/kimi-coding.js.map +1 -1
- package/dist/core/usage-adapters/openai-codex.d.ts +3 -2
- package/dist/core/usage-adapters/openai-codex.d.ts.map +1 -1
- package/dist/core/usage-adapters/openai-codex.js +3 -3
- package/dist/core/usage-adapters/openai-codex.js.map +1 -1
- package/dist/core/usage-adapters/xai.d.ts +2 -2
- package/dist/core/usage-adapters/xai.d.ts.map +1 -1
- package/dist/core/usage-adapters/xai.js +3 -3
- package/dist/core/usage-adapters/xai.js.map +1 -1
- package/dist/core/usage-adapters/zai.d.ts +2 -2
- package/dist/core/usage-adapters/zai.d.ts.map +1 -1
- package/dist/core/usage-adapters/zai.js +2 -2
- package/dist/core/usage-adapters/zai.js.map +1 -1
- package/dist/core/usage-service.d.ts +9 -2
- package/dist/core/usage-service.d.ts.map +1 -1
- package/dist/core/usage-service.js +95 -50
- package/dist/core/usage-service.js.map +1 -1
- package/dist/gateway/agent-bridge.d.ts +9 -1
- package/dist/gateway/agent-bridge.d.ts.map +1 -1
- package/dist/gateway/agent-bridge.js +91 -37
- package/dist/gateway/agent-bridge.js.map +1 -1
- package/dist/gateway/buttons.d.ts +8 -1
- package/dist/gateway/buttons.d.ts.map +1 -1
- package/dist/gateway/buttons.js +76 -18
- package/dist/gateway/buttons.js.map +1 -1
- package/dist/gateway/commands.d.ts +1 -0
- package/dist/gateway/commands.d.ts.map +1 -1
- package/dist/gateway/commands.js +10 -4
- package/dist/gateway/commands.js.map +1 -1
- package/dist/gateway/cron.d.ts +12 -11
- package/dist/gateway/cron.d.ts.map +1 -1
- package/dist/gateway/cron.js +65 -56
- package/dist/gateway/cron.js.map +1 -1
- package/dist/gateway/index.d.ts.map +1 -1
- package/dist/gateway/index.js +2 -1
- package/dist/gateway/index.js.map +1 -1
- package/dist/gateway/mobile-commands.d.ts +1 -0
- package/dist/gateway/mobile-commands.d.ts.map +1 -1
- package/dist/gateway/mobile-commands.js +56 -14
- package/dist/gateway/mobile-commands.js.map +1 -1
- package/dist/gateway/presenter.d.ts +12 -2
- package/dist/gateway/presenter.d.ts.map +1 -1
- package/dist/gateway/presenter.js +73 -38
- package/dist/gateway/presenter.js.map +1 -1
- package/dist/gateway/router.d.ts +2 -2
- package/dist/gateway/router.d.ts.map +1 -1
- package/dist/gateway/router.js +17 -2
- package/dist/gateway/router.js.map +1 -1
- package/dist/gateway/service.d.ts +2 -0
- package/dist/gateway/service.d.ts.map +1 -1
- package/dist/gateway/service.js +153 -0
- package/dist/gateway/service.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +86 -33
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +6 -4
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/copyable-text.d.ts.map +1 -1
- package/dist/modes/interactive/components/copyable-text.js +3 -2
- package/dist/modes/interactive/components/copyable-text.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +96 -109
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/smooth-streaming.d.ts.map +1 -1
- package/dist/modes/interactive/smooth-streaming.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/node-runtime/{adapter-VFG7WNPX.js → adapter-247RTYLS.js} +4 -5
- package/dist/node-runtime/agent-session-services-KDMSPQDY.js +53 -0
- package/dist/node-runtime/{brave-UWDA3I22.js → brave-2AFFAXNN.js} +1 -1
- package/dist/node-runtime/{builtin-extensions-I2HIFEMY.js → builtin-extensions-OX6KELNX.js} +8 -7
- package/dist/node-runtime/{chunk-QH6YL7VK.js → chunk-252ZVPPF.js} +3 -3
- package/dist/node-runtime/{chunk-PCEW76PM.js → chunk-2OIPYKLZ.js} +6 -6
- package/dist/node-runtime/{chunk-XBWPBZBN.js → chunk-2SWW7KMW.js} +1 -1
- package/dist/node-runtime/{chunk-UQUCFAJ3.js → chunk-2VIW3IMF.js} +40 -7
- package/dist/node-runtime/{chunk-UOEDI7RP.js → chunk-4KNKQGHM.js} +2 -2
- package/dist/node-runtime/{chunk-FQMM2EZ2.js → chunk-64XTQNLA.js} +29 -7
- package/dist/node-runtime/{chunk-SPVDBUOS.js → chunk-6O3IW3IA.js} +2 -2
- package/dist/node-runtime/{chunk-5ZQXRPMC.js → chunk-6ZZHGUSA.js} +2 -2
- package/dist/node-runtime/chunk-A2WO6WIN.js +1197 -0
- package/dist/node-runtime/{chunk-WIF24I7G.js → chunk-APVM5NEU.js} +149 -144
- package/dist/node-runtime/{chunk-RCR3XAQY.js → chunk-AYHH7YSD.js} +177 -144
- package/dist/node-runtime/{chunk-LWYIVUGV.js → chunk-B2IBAXNX.js} +48 -31
- package/dist/node-runtime/{chunk-M3LZLQW2.js → chunk-BDRGN3L2.js} +1 -0
- package/dist/node-runtime/{chunk-GJ2RDBRL.js → chunk-BYC6RKCN.js} +4 -2
- package/dist/node-runtime/{chunk-UVWCDQYF.js → chunk-C3P6Q3B2.js} +455 -278
- package/dist/node-runtime/{chunk-SN2KSMPQ.js → chunk-DQSCFU7R.js} +13 -7
- package/dist/node-runtime/{chunk-4SBQAI4T.js → chunk-EBLAZVSA.js} +2058 -73
- package/dist/node-runtime/{chunk-2KIPT4HJ.js → chunk-EPR2F3GU.js} +68 -20
- package/dist/node-runtime/{chunk-CSFI6YXL.js → chunk-ES6PBSIE.js} +1 -1
- package/dist/node-runtime/{chunk-IF24A3FH.js → chunk-EVM653DD.js} +4 -4
- package/dist/node-runtime/{chunk-HTFRRDSF.js → chunk-F2SMQ6ES.js} +3 -2
- package/dist/node-runtime/chunk-F2V2F4ZG.js +114 -0
- package/dist/node-runtime/{chunk-IKU6Y7DL.js → chunk-FE3WZI7F.js} +9 -6
- package/dist/node-runtime/{chunk-GYIXXQAO.js → chunk-FIRA54AC.js} +2 -5
- package/dist/node-runtime/{chunk-6VYZ5UFP.js → chunk-FLGEEH27.js} +1 -1
- package/dist/node-runtime/{chunk-5EFIU3OM.js → chunk-FTKEZL27.js} +1 -1
- package/dist/node-runtime/{chunk-RGQAEOM5.js → chunk-FYIFPFXE.js} +23 -6
- package/dist/node-runtime/{chunk-HMBGROQK.js → chunk-GLDXBBL6.js} +3 -3
- package/dist/node-runtime/{chunk-KWA4OC2J.js → chunk-H6YBCWKG.js} +1 -1
- package/dist/node-runtime/{chunk-2MI5OPMN.js → chunk-HNNHHJAB.js} +68 -50
- package/dist/node-runtime/{chunk-I74LN5JG.js → chunk-HUD2RFJW.js} +2 -2
- package/dist/node-runtime/{chunk-I54LGHWM.js → chunk-I2H5QMWW.js} +3 -3
- package/dist/node-runtime/{chunk-XHIRAZ26.js → chunk-IAZSK7KK.js} +1 -1
- package/dist/node-runtime/{chunk-X3PGBLQ7.js → chunk-IHHTWSXN.js} +9 -9
- package/dist/node-runtime/{chunk-DAPBPCBP.js → chunk-IPP5BIV5.js} +25 -3
- package/dist/node-runtime/{chunk-QFWG2RVL.js → chunk-IXG6F4NT.js} +11 -0
- package/dist/node-runtime/{chunk-RDDAZOMU.js → chunk-JL4HYQXK.js} +1 -1
- package/dist/node-runtime/{chunk-CDVHZOOK.js → chunk-JQYNHAKX.js} +1 -1
- package/dist/node-runtime/{chunk-L24TPBS4.js → chunk-JWEVT363.js} +37 -29
- package/dist/node-runtime/{chunk-2HMIRK7W.js → chunk-LHV3V67B.js} +5 -5
- package/dist/node-runtime/{chunk-UAEOTO52.js → chunk-MNQXDFPV.js} +6 -2
- package/dist/node-runtime/{chunk-T4DA4XE7.js → chunk-MQPKUHRN.js} +162 -82
- package/dist/node-runtime/{chunk-MPKSOJBT.js → chunk-NHATCSOI.js} +4 -7
- package/dist/node-runtime/{chunk-RMQZU2YU.js → chunk-NNNR3QIY.js} +1 -1
- package/dist/node-runtime/{chunk-RWTKOC3B.js → chunk-QBTJG3UM.js} +1 -1
- package/dist/node-runtime/{chunk-P7XBVXGT.js → chunk-QNVMKNNN.js} +18 -14
- package/dist/node-runtime/{chunk-GS42T6MX.js → chunk-QW3AAHWN.js} +1 -1
- package/dist/node-runtime/{chunk-DVQSHCVP.js → chunk-R4WSB2MQ.js} +5 -5
- package/dist/node-runtime/{chunk-B4HP6ZWM.js → chunk-RHCGYWO7.js} +1 -1
- package/dist/node-runtime/{chunk-2CFDTH5B.js → chunk-RLGY2QIL.js} +45 -59
- package/dist/node-runtime/{chunk-KKUTQQV7.js → chunk-RYRGL7P4.js} +1 -1
- package/dist/node-runtime/{chunk-5Q4A37RB.js → chunk-TCT6PCUR.js} +32 -11
- package/dist/node-runtime/{chunk-D5UCKPBQ.js → chunk-UBWW2PID.js} +1 -1
- package/dist/node-runtime/{chunk-E75WFZ33.js → chunk-UNUZICC4.js} +3 -3
- package/dist/node-runtime/{chunk-RAK2QA2B.js → chunk-UYRKUKFE.js} +4 -4
- package/dist/node-runtime/{chunk-LWJXTA6Z.js → chunk-VSUEAI7O.js} +1377 -45
- package/dist/node-runtime/{chunk-NAY6UUQZ.js → chunk-WDIVLMOH.js} +1 -1
- package/dist/node-runtime/{chunk-E24EO34J.js → chunk-WPXUR3HE.js} +1 -1
- package/dist/node-runtime/{chunk-SLVOAQOY.js → chunk-WXYNALHD.js} +113 -1
- package/dist/node-runtime/{chunk-KJ6DWS3K.js → chunk-XEOCUEUJ.js} +121 -60
- package/dist/node-runtime/chunk-YD3IZAZ7.js +47 -0
- package/dist/node-runtime/chunk-YSB6URMT.js +41 -0
- package/dist/node-runtime/{chunk-LJTIVM7I.js → chunk-ZDJUR45D.js} +427 -126
- package/dist/node-runtime/{chunk-YWOJIGFY.js → chunk-ZEVFBFNF.js} +95 -142
- package/dist/node-runtime/{chunk-5B6VKFEV.js → chunk-ZIQUS5QO.js} +1 -1
- package/dist/node-runtime/cli-runtime.js +4 -4
- package/dist/node-runtime/{command-FSBU2EVZ.js → command-3HMSY6W2.js} +5 -5
- package/dist/node-runtime/{commands-EMBQQVVT.js → commands-D6A6H4TS.js} +11 -18
- package/dist/node-runtime/{commands-NAAL7KNT.js → commands-OCSSEEWJ.js} +16 -16
- package/dist/node-runtime/{config-QDD5PUE2.js → config-CU7M7BTK.js} +1 -1
- package/dist/node-runtime/{config-D4QUPAHX.js → config-DS7XLMEM.js} +2 -2
- package/dist/node-runtime/{direct-tool-executor-4D7UXYU4.js → direct-tool-executor-2QJXVQD2.js} +6 -11
- package/dist/node-runtime/{exa-LL64QZR2.js → exa-6VBZNPGE.js} +1 -1
- package/dist/node-runtime/{export-html-VM4J2MNR.js → export-html-BRWF5AHI.js} +4 -4
- package/dist/node-runtime/{extension-input-E7BJZ35H.js → extension-input-VBBMX7YV.js} +5 -5
- package/dist/node-runtime/{extension-selector-WU42XDTU.js → extension-selector-T5AOG5XB.js} +5 -5
- package/dist/node-runtime/{extract-EQJE7FZZ.js → extract-BFX2N35A.js} +3 -3
- package/dist/node-runtime/{gateway-DQM333YV.js → gateway-6623C2U3.js} +51 -42
- package/dist/node-runtime/{gemini-search-35OCRFLN.js → gemini-search-C66QHDT6.js} +2 -2
- package/dist/node-runtime/{gemini-url-context-UQY3PHHW.js → gemini-url-context-T7DEK73R.js} +3 -3
- package/dist/node-runtime/{github-extract-F64RZOEA.js → github-extract-HOGE3D6F.js} +107 -38
- package/dist/node-runtime/{goal-NJEE5CTU.js → goal-AIHEDUYA.js} +5 -5
- package/dist/node-runtime/index.js +50 -48
- package/dist/node-runtime/{init-P5AUV77I.js → init-JBQJIPYI.js} +3 -6
- package/dist/node-runtime/{install-cli-NN4YAU44.js → install-cli-INGHIVBY.js} +30 -25
- package/dist/node-runtime/interactive-mode-FM5DNUSW.js +74 -0
- package/dist/node-runtime/{interactive-view-NGFAMHPW.js → interactive-view-K33PM4OS.js} +8 -8
- package/dist/node-runtime/{lsp-manager-3ASUSO4T.js → lsp-manager-S554V5HR.js} +240 -155
- package/dist/node-runtime/{lunr-browser-IR7TL3J7.js → lunr-browser-H4FDOR3C.js} +7 -7
- package/dist/node-runtime/{lunr-computer-use-Y23CCDIJ.js → lunr-computer-use-VDP2QNRQ.js} +9 -9
- package/dist/node-runtime/{lunr-cron-USHC4LXH.js → lunr-cron-LKVA5CZZ.js} +93 -80
- package/dist/node-runtime/main.js +42 -41
- package/dist/node-runtime/{mobile-commands-O5LCHL32.js → mobile-commands-XT3NQXLD.js} +12 -12
- package/dist/node-runtime/{model-runtime-OO5HFLWE.js → model-runtime-MZ2JMDV4.js} +4 -4
- package/dist/node-runtime/{openai-search-B3MSNCQA.js → openai-search-K72LJDYN.js} +1 -1
- package/dist/node-runtime/{package-manager-cli-Q7BL6KZO.js → package-manager-cli-CYM262SI.js} +20 -20
- package/dist/node-runtime/{parallel-AI2MNSCU.js → parallel-X724NCNU.js} +1 -1
- package/dist/node-runtime/{perplexity-QVJW3I35.js → perplexity-RDHQHVMK.js} +2 -2
- package/dist/node-runtime/{pi-intercom-QM53RLTS.js → pi-intercom-NHHEPBMI.js} +2 -2
- package/dist/node-runtime/{pi-mcp-adapter-3PHF4KZ4.js → pi-mcp-adapter-PP3GQJQ2.js} +98 -94
- package/dist/node-runtime/{pi-ollama-cloud-2F3Y2QNM.js → pi-ollama-cloud-4MNWIMFN.js} +11 -24
- package/dist/node-runtime/{pi-prompt-template-model-QWTCGLJU.js → pi-prompt-template-model-FTGELLS5.js} +32 -36
- package/dist/node-runtime/{pi-subagents-7IBSSGEW.js → pi-subagents-LT3CTO4E.js} +98 -106
- package/dist/node-runtime/{pi-web-access-WTGMNZ32.js → pi-web-access-Z3LP246C.js} +144 -108
- package/dist/node-runtime/{print-mode-XQ76CYMT.js → print-mode-2XHN5SUA.js} +3 -3
- package/dist/node-runtime/{process-registry-4FAKZQOL.js → process-registry-Q4QWIU33.js} +3 -3
- package/dist/node-runtime/{proxy-modes-5FAPSZEZ.js → proxy-modes-VEGC6QG5.js} +8 -15
- package/dist/node-runtime/rpc-entry.js +42 -41
- package/dist/node-runtime/{rpc-mode-O3XV4SID.js → rpc-mode-32DHJPEJ.js} +4 -4
- package/dist/node-runtime/{runtime-4VA6JKNF.js → runtime-EDUUGAQD.js} +56 -46
- package/dist/node-runtime/{runtime-EFPPD2UW.js → runtime-UGXM4NPQ.js} +2 -2
- package/dist/node-runtime/{runtime-bridges-7WVCLI76.js → runtime-bridges-DNKMNJCF.js} +7 -7
- package/dist/node-runtime/{service-MDQIPQ26.js → service-IHLGFLBT.js} +6 -4
- package/dist/node-runtime/{session-handoff-GWARVEWT.js → session-handoff-LNSPDTKY.js} +4 -4
- package/dist/node-runtime/{session-manager-JB5J6XBN.js → session-manager-YIFIAVGC.js} +3 -3
- package/dist/node-runtime/{session-selector-GIM4DHQ2.js → session-selector-I73U3EIO.js} +7 -7
- package/dist/node-runtime/{settings-manager-DJDM5AHM.js → settings-manager-P2TKGYTR.js} +5 -5
- package/dist/node-runtime/{setup-JBKOLJ6F.js → setup-H7CCELIL.js} +15 -1
- package/dist/node-runtime/{setup-PFBWRNUI.js → setup-OLQ4GXWS.js} +17 -17
- package/dist/node-runtime/{src-IVPNZ2BX.js → src-6RV4PXKJ.js} +64 -36
- package/dist/node-runtime/{subagent-executor-LPZ4BHWE.js → subagent-executor-Q5F7DBVM.js} +119 -58
- package/dist/node-runtime/{tavily-WEZFVFP7.js → tavily-KZBFYQB6.js} +1 -1
- package/dist/node-runtime/{theme-UOO5RVFN.js → theme-YLVZKKZD.js} +2 -2
- package/dist/node-runtime/{trust-manager-MMISNCYO.js → trust-manager-ZCU76D23.js} +2 -2
- package/dist/node-runtime/{update-cli-HNMOTNGX.js → update-cli-F3XXJANQ.js} +6 -6
- package/dist/node-runtime/{usage-service-LFB2SWYV.js → usage-service-CPAGBED5.js} +1 -1
- package/dist/node-runtime/{video-extract-OOZHR7SU.js → video-extract-UPKYIBW3.js} +3 -3
- package/dist/node-runtime/{workflow-OHPBEFO4.js → workflow-ICDI4N3J.js} +208 -78
- package/dist/node-runtime/{youtube-extract-FNPAUNHU.js → youtube-extract-77NT7FWH.js} +4 -4
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/exif-orientation.d.ts.map +1 -1
- package/dist/utils/exif-orientation.js +25 -10
- package/dist/utils/exif-orientation.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +2 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +33 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/sanitize-terminal-text.d.ts +7 -0
- package/dist/utils/sanitize-terminal-text.d.ts.map +1 -0
- package/dist/utils/sanitize-terminal-text.js +67 -0
- package/dist/utils/sanitize-terminal-text.js.map +1 -0
- package/docs/browser.md +1 -1
- package/docs/custom-provider.md +3 -3
- package/docs/extensions.md +8 -4
- package/docs/features.md +15 -7
- package/docs/keybindings.md +1 -1
- package/docs/models.md +5 -5
- package/docs/packages.md +1 -1
- package/docs/providers.md +2 -2
- package/docs/rpc.md +7 -7
- package/docs/security.md +10 -5
- package/docs/sessions.md +2 -2
- package/docs/skills.md +1 -1
- package/docs/terminal-setup.md +7 -7
- package/docs/themes.md +1 -1
- package/docs/usage.md +1 -1
- package/examples/extensions/auto-commit-on-exit.ts +1 -1
- package/examples/extensions/bash-spawn-hook.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/claude-rules.ts +1 -1
- package/examples/extensions/commands.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/index.ts +3 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/doom-overlay/index.ts +1 -1
- package/examples/extensions/git-merge-and-resolve.ts +2 -2
- package/examples/extensions/gondolin/index.ts +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/hidden-thinking-label.ts +1 -1
- package/examples/extensions/inline-bash.ts +2 -2
- package/examples/extensions/input-transform-streaming.ts +2 -2
- package/examples/extensions/input-transform.ts +2 -2
- package/examples/extensions/interactive-shell.ts +1 -1
- package/examples/extensions/kimi-deferred-tools.ts +1 -1
- package/examples/extensions/mac-system-theme.ts +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/model-status.ts +1 -1
- package/examples/extensions/notify.ts +3 -3
- package/examples/extensions/overlay-qa-tests.ts +1 -1
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/pirate.ts +1 -1
- package/examples/extensions/preset.ts +3 -3
- package/examples/extensions/project-trust.ts +4 -4
- package/examples/extensions/prompt-customizer.ts +1 -1
- package/examples/extensions/rainbow-editor.ts +1 -1
- package/examples/extensions/rpc-demo.ts +1 -1
- package/examples/extensions/sandbox/index.ts +7 -7
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/shutdown-command.ts +5 -5
- package/examples/extensions/ssh.ts +2 -2
- package/examples/extensions/subagent/README.md +3 -3
- package/examples/extensions/subagent/index.ts +2 -2
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/working-indicator.ts +1 -1
- package/examples/extensions/working-message-test.ts +1 -1
- package/examples/sdk/01-minimal.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +1 -1
- package/examples/sdk/04-skills.ts +1 -1
- package/examples/sdk/06-extensions.ts +2 -2
- package/examples/sdk/08-prompt-templates.ts +1 -1
- package/install-lock/package-lock.json +24 -24
- package/install-lock/package.json +2 -2
- package/npm-shrinkwrap.json +60 -24
- package/package.json +9 -9
- package/dist/modes/interactive/components/exit-card.d.ts +0 -22
- package/dist/modes/interactive/components/exit-card.d.ts.map +0 -1
- package/dist/modes/interactive/components/exit-card.js +0 -69
- package/dist/modes/interactive/components/exit-card.js.map +0 -1
- package/dist/node-runtime/agent-session-services-5YXS2PLF.js +0 -51
- package/dist/node-runtime/chunk-2IDTZAG5.js +0 -1415
- package/dist/node-runtime/chunk-3MISGG4S.js +0 -769
- package/dist/node-runtime/chunk-4HXHDDCF.js +0 -16
- package/dist/node-runtime/chunk-5B5QN3ZQ.js +0 -356
- package/dist/node-runtime/chunk-7IQGBKBJ.js +0 -2033
- package/dist/node-runtime/chunk-AUXVGBO4.js +0 -19
- package/dist/node-runtime/chunk-PTFBPO3C.js +0 -50
- package/dist/node-runtime/interactive-mode-YOHZVMKX.js +0 -74
- package/dist/node-runtime/mcp-auth-flow-QVLFVLEO.js +0 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/core/cron/jobs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: cron job model + persistent store (Phase 1 of the cron/gateway roadmap).\n *\n * Jobs persist at `<agentDir>/cron/jobs.json` (`{ jobs: [...], updated_at }`),\n * agentDir resolved via getAgentDir() (honors PI_CODING_AGENT_DIR, defaults\n * ~/.lunr/agent). Writes are atomic (tmp file in the same dir + rename) and\n * serialized through an in-process promise queue — single-process, no locks.\n *\n * Per-run output goes to `<agentDir>/cron/output/<jobId>/<YYYY-MM-DD_HH-MM-SS>.md`\n * with a retention cap of the newest 50 files per job.\n *\n * Tests inject a base dir via setCronBaseDir() so no real agent dir is touched.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, normalize, resolve } from \"node:path\";\nimport { Cron } from \"croner\";\nimport { getAgentDir } from \"../../config.ts\";\nimport {\n\tchatPlatformDeliverBlockedMessage,\n\tdeliverMentionsChatPlatform,\n\tisFeatureEnabled,\n} from \"../install-features.ts\";\n\n// ---------------------------------------------------------------------------\n// Model\n// ---------------------------------------------------------------------------\n\nexport type CronScheduleKind = \"once\" | \"interval\" | \"cron\";\n\nexport interface CronSchedule {\n\tkind: CronScheduleKind;\n\t/** ISO timestamp, one-shots only. */\n\trunAt?: string;\n\t/** Interval length in minutes, interval kind only. */\n\tminutes?: number;\n\t/** 5-field cron expression, cron kind only. */\n\texpr?: string;\n}\n\nexport interface CronJobOrigin {\n\tplatform: string;\n\tchatId: string;\n\tthreadId?: string;\n\tchatType?: string;\n}\n\nexport type CronJobState = \"scheduled\" | \"paused\" | \"completed\" | \"error\";\nexport type CronJobStatus = \"ok\" | \"error\";\n\nexport interface CronJob {\n\tid: string;\n\tname: string;\n\tprompt: string;\n\tschedule: CronSchedule;\n\tscheduleDisplay: string;\n\trepeat: { times: number | null; completed: number };\n\tenabled: boolean;\n\tstate: CronJobState;\n\tcreatedAt: string;\n\tnextRunAt: string | null;\n\tlastRunAt: string | null;\n\tlastStatus: CronJobStatus | null;\n\tlastError: string | null;\n\tlastDeliveryError: string | null;\n\t/** \"local\" | \"origin\" | \"telegram[:chatId]\" | \"discord[:chatId]\" (comma-combinable). v1 TUI only uses \"local\". */\n\tdeliver: string;\n\torigin?: CronJobOrigin | null;\n\t/** Job IDs whose latest output (8K chars cap each) is prepended to the prompt. */\n\tcontextFrom?: string[];\n\tworkdir?: string;\n}\n\nexport interface CreateJobInput {\n\tprompt: string;\n\t/** Raw schedule input, parsed by parseSchedule(). */\n\tschedule: string;\n\tname?: string;\n\t/** Repetition cap; null = forever. One-shots are always times=1. */\n\ttimes?: number | null;\n\tdeliver?: string;\n\tcontextFrom?: string[];\n\tworkdir?: string;\n\torigin?: CronJobOrigin | null;\n}\n\nexport interface JobPatch {\n\tname?: string;\n\tprompt?: string;\n\t/** Raw schedule input; reparsed and re-anchors nextRunAt. */\n\tschedule?: string;\n\tdeliver?: string;\n\tenabled?: boolean;\n\ttimes?: number | null;\n\tcontextFrom?: string[];\n\tworkdir?: string;\n\torigin?: CronJobOrigin | null;\n\t/** Internal: manual-run trigger and delivery-error bookkeeping. */\n\tnextRunAt?: string | null;\n\tlastDeliveryError?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Flat recovery grace for one-shots: fire up to 120s late. */\nexport const ONE_SHOT_GRACE_MS = 120_000;\n/** Recurring grace window clamps: at least 120s, at most 2h. */\nconst MIN_GRACE_MS = 120_000;\nconst MAX_GRACE_MS = 7_200_000;\n/** Per-job output retention. */\nexport const OUTPUT_RETENTION = 50;\n/** Cap per upstream job output injected via contextFrom. */\nexport const CONTEXT_OUTPUT_CAP = 8000;\n\nconst MAX_NAME_LEN = 50;\n\n// ---------------------------------------------------------------------------\n// Paths + store state\n// ---------------------------------------------------------------------------\n\nlet baseDirOverride: string | undefined;\nlet cache: CronJob[] | undefined;\nlet writeQueue: Promise<void> = Promise.resolve();\n\n/**\n * Override the base dir (defaults to the agent dir) and reset the in-memory\n * cache + write queue. Passing undefined restores the default. For tests.\n */\nexport function setCronBaseDir(dir: string | undefined): void {\n\tbaseDirOverride = dir;\n\tcache = undefined;\n\twriteQueue = Promise.resolve();\n}\n\nfunction cronDir(): string {\n\treturn join(baseDirOverride ?? getAgentDir(), \"cron\");\n}\n\nfunction jobsFile(): string {\n\treturn join(cronDir(), \"jobs.json\");\n}\n\nfunction outputDir(jobId: string): string {\n\treturn join(cronDir(), \"output\", jobId);\n}\n\nfunction loadJobs(): CronJob[] {\n\tif (cache) return cache;\n\tconst file = jobsFile();\n\tif (!existsSync(file)) {\n\t\tcache = [];\n\t\treturn cache;\n\t}\n\ttry {\n\t\tconst parsed = JSON.parse(readFileSync(file, \"utf-8\"));\n\t\tcache = Array.isArray(parsed?.jobs) ? (parsed.jobs as CronJob[]) : [];\n\t} catch {\n\t\tcache = [];\n\t}\n\treturn cache;\n}\n\n/** Queue an atomic write of the in-memory store. Rejects only for the caller; the chain self-heals. */\nfunction persist(): Promise<void> {\n\tconst snapshot = JSON.stringify({ jobs: cache ?? [], updated_at: new Date().toISOString() }, null, 2);\n\tconst p = writeQueue.then(() => {\n\t\tmkdirSync(cronDir(), { recursive: true });\n\t\tconst tmp = join(cronDir(), `.jobs.json.${process.pid}.${Date.now()}.tmp`);\n\t\twriteFileSync(tmp, snapshot, \"utf-8\");\n\t\trenameSync(tmp, jobsFile());\n\t});\n\twriteQueue = p.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn p;\n}\n\n// ---------------------------------------------------------------------------\n// Schedule parsing\n// ---------------------------------------------------------------------------\n\nexport interface ParsedSchedule {\n\tschedule: CronSchedule;\n\tscheduleDisplay: string;\n}\n\nconst INTERVAL_RE = /^every\\s+(\\d+)\\s*([mhd])$/i;\nconst DURATION_RE = /^(\\d+)\\s*([mhd])$/i;\n\nfunction toMinutes(value: number, unit: string): number {\n\tconst u = unit.toLowerCase();\n\tconst factor = u === \"d\" ? 1440 : u === \"h\" ? 60 : 1;\n\treturn value * factor;\n}\n\n/**\n * Parse a schedule input:\n * - `every 30m` / `every 2h` / `every 1d` → interval (minutes; d = 1440)\n * - bare `30m` / `2h` / `1d` → one-shot at now + duration\n * - ISO timestamp → one-shot at that time\n * - 5-field cron expression (validated via croner; throws on invalid)\n */\nexport function parseSchedule(input: string, now: Date = new Date()): ParsedSchedule {\n\tconst text = input.trim().replace(/\\s+/g, \" \");\n\tif (!text) throw new Error(\"schedule is empty\");\n\n\tconst interval = INTERVAL_RE.exec(text);\n\tif (interval) {\n\t\tconst minutes = toMinutes(Number(interval[1]), interval[2]);\n\t\tif (minutes <= 0) throw new Error(`invalid interval: \"${text}\"`);\n\t\treturn {\n\t\t\tschedule: { kind: \"interval\", minutes },\n\t\t\tscheduleDisplay: `every ${Number(interval[1])}${interval[2].toLowerCase()}`,\n\t\t};\n\t}\n\n\tconst duration = DURATION_RE.exec(text);\n\tif (duration) {\n\t\tconst minutes = toMinutes(Number(duration[1]), duration[2]);\n\t\tif (minutes <= 0) throw new Error(`invalid duration: \"${text}\"`);\n\t\tconst runAt = new Date(now.getTime() + minutes * 60_000).toISOString();\n\t\treturn { schedule: { kind: \"once\", runAt }, scheduleDisplay: text };\n\t}\n\n\tif (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\n\t\tconst ts = Date.parse(text);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\tconst runAt = new Date(ts).toISOString();\n\t\t\treturn { schedule: { kind: \"once\", runAt }, scheduleDisplay: runAt };\n\t\t}\n\t}\n\n\tif (text.split(\" \").length === 5) {\n\t\ttry {\n\t\t\tnew Cron(text);\n\t\t} catch (err) {\n\t\t\tthrow new Error(`invalid cron expression \"${text}\": ${err instanceof Error ? err.message : String(err)}`);\n\t\t}\n\t\treturn { schedule: { kind: \"cron\", expr: text }, scheduleDisplay: text };\n\t}\n\n\tthrow new Error(\n\t\t`unrecognized schedule: \"${input}\" (expected \"every 30m\", \"2h\", an ISO timestamp, or a 5-field cron expression)`,\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Next-run computation\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the next run time for a job:\n * - once: the stored runAt, recoverable within a 120s grace if never run; null after that or once run.\n * - interval: (lastRunAt ?? createdAt) + minutes.\n * - cron: croner nextRun after (lastRunAt ?? now).\n */\nexport function computeNextRun(job: CronJob, now: Date = new Date()): Date | null {\n\tconst s = job.schedule;\n\tif (s.kind === \"once\") {\n\t\tif (job.repeat.completed > 0) return null;\n\t\tif (!s.runAt) return null;\n\t\tconst runAt = new Date(s.runAt);\n\t\tif (Number.isNaN(runAt.getTime())) return null;\n\t\treturn now.getTime() - runAt.getTime() <= ONE_SHOT_GRACE_MS ? runAt : null;\n\t}\n\tif (s.kind === \"interval\") {\n\t\tconst base = job.lastRunAt ? new Date(job.lastRunAt) : new Date(job.createdAt);\n\t\treturn new Date(base.getTime() + (s.minutes ?? 0) * 60_000);\n\t}\n\tconst cron = new Cron(s.expr!);\n\treturn cron.nextRun(job.lastRunAt ? new Date(job.lastRunAt) : now);\n}\n\n/** Approximate schedule period in ms (recurring jobs only) for grace-window sizing. */\nfunction periodMs(job: CronJob, now: Date): number {\n\tif (job.schedule.kind === \"interval\") return (job.schedule.minutes ?? 0) * 60_000;\n\tif (job.schedule.kind === \"cron\") {\n\t\tconst cron = new Cron(job.schedule.expr!);\n\t\tconst a = cron.nextRun(now);\n\t\tif (!a) return MAX_GRACE_MS;\n\t\tconst b = cron.nextRun(a);\n\t\tif (!b) return MAX_GRACE_MS;\n\t\treturn Math.max(b.getTime() - a.getTime(), 1);\n\t}\n\treturn 0;\n}\n\n/** Next slot strictly after `after` for a recurring job. */\nfunction nextSlotAfter(job: CronJob, after: Date): Date | null {\n\tif (job.schedule.kind === \"interval\") {\n\t\tconst period = (job.schedule.minutes ?? 0) * 60_000;\n\t\tif (period <= 0) return null;\n\t\t// Step forward from the stored nextRunAt (or now) until strictly in the future of `after`.\n\t\tlet slot = job.nextRunAt ? new Date(job.nextRunAt).getTime() : new Date(job.createdAt).getTime() + period;\n\t\twhile (slot <= after.getTime()) slot += period;\n\t\treturn new Date(slot);\n\t}\n\tif (job.schedule.kind === \"cron\") {\n\t\treturn new Cron(job.schedule.expr!).nextRun(after);\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nlet workdirRoots: string[] = [process.cwd(), homedir()];\nlet deliverValidator: ((deliver: string, origin?: CronJobOrigin | null) => string | undefined) | undefined;\n\n/**\n * Configure the allowed absolute roots for cron job workdirs. Relative workdirs\n * are resolved against process.cwd() and must fall under one of these roots.\n * Default roots are process.cwd() and the user's home directory.\n */\nexport function setCronWorkdirRoots(roots: string[]): void {\n\tworkdirRoots = roots.map((r) => normalize(resolve(r)));\n}\n\n/**\n * Configure a deliver-target validator. When set, createJob/updateJob reject\n * deliver strings that the validator does not approve. The gateway uses this\n * to prevent delivery to arbitrary chats.\n */\nexport function setCronDeliverValidator(\n\tfn: ((deliver: string, origin?: CronJobOrigin | null) => string | undefined) | undefined,\n): void {\n\tdeliverValidator = fn;\n}\n\n/** Test seam: restore default validation state. */\nexport function resetCronValidators(): void {\n\tworkdirRoots = [process.cwd(), homedir()];\n\tdeliverValidator = undefined;\n}\n\nfunction isPathUnderRoot(absPath: string, root: string): boolean {\n\tconst normRoot = normalize(root).replace(/\\\\$/g, \"\");\n\tconst normPath = normalize(absPath).replace(/\\\\$/g, \"\");\n\treturn normPath === normRoot || normPath.startsWith(`${normRoot}/`) || normPath.startsWith(`${normRoot}\\\\`);\n}\n\nfunction validateWorkdir(workdir: string): string | undefined {\n\tconst candidate = resolve(workdir);\n\tif (isAbsolute(workdir) && !workdirRoots.some((root) => isPathUnderRoot(candidate, root))) {\n\t\treturn `workdir ${workdir} is outside allowed roots`;\n\t}\n\tif (!workdirRoots.some((root) => isPathUnderRoot(candidate, root))) {\n\t\treturn `workdir ${workdir} resolves outside allowed roots`;\n\t}\n\treturn undefined;\n}\n\nfunction runDeliverValidation(deliver: string, origin?: CronJobOrigin | null): string | undefined {\n\tif (deliverMentionsChatPlatform(deliver) && !isFeatureEnabled(\"chat-platforms\")) {\n\t\treturn chatPlatformDeliverBlockedMessage();\n\t}\n\treturn deliverValidator?.(deliver, origin);\n}\n\nfunction deriveName(prompt: string): string {\n\tconst first = prompt.split(\"\\n\")[0].replace(/\\s+/g, \" \").trim();\n\treturn (first || \"cron job\").slice(0, MAX_NAME_LEN);\n}\n\nexport async function createJob(input: CreateJobInput): Promise<CronJob> {\n\tconst prompt = input.prompt.trim();\n\tif (!prompt) throw new Error(\"createJob: prompt is empty\");\n\tif (input.workdir) {\n\t\tconst err = validateWorkdir(input.workdir);\n\t\tif (err) throw new Error(`createJob: ${err}`);\n\t}\n\tif (input.deliver) {\n\t\tconst err = runDeliverValidation(input.deliver, input.origin);\n\t\tif (err) throw new Error(`createJob: ${err}`);\n\t}\n\tconst now = new Date();\n\tconst parsed = parseSchedule(input.schedule, now);\n\tconst isOneShot = parsed.schedule.kind === \"once\";\n\tconst times = isOneShot ? 1 : (input.times ?? null);\n\tif (times !== null && (!Number.isInteger(times) || times < 1)) {\n\t\tthrow new Error(`createJob: times must be a positive integer or null, got ${times}`);\n\t}\n\tconst job: CronJob = {\n\t\tid: randomUUID().replaceAll(\"-\", \"\").slice(0, 12),\n\t\tname: (input.name?.trim() || deriveName(prompt)).slice(0, MAX_NAME_LEN),\n\t\tprompt,\n\t\tschedule: parsed.schedule,\n\t\tscheduleDisplay: parsed.scheduleDisplay,\n\t\trepeat: { times, completed: 0 },\n\t\tenabled: true,\n\t\tstate: \"scheduled\",\n\t\tcreatedAt: now.toISOString(),\n\t\tnextRunAt: null,\n\t\tlastRunAt: null,\n\t\tlastStatus: null,\n\t\tlastError: null,\n\t\tlastDeliveryError: null,\n\t\tdeliver: input.deliver?.trim() || \"local\",\n\t\torigin: input.origin ?? null,\n\t\tcontextFrom: input.contextFrom,\n\t\tworkdir: input.workdir,\n\t};\n\tjob.nextRunAt = computeNextRun(job, now)?.toISOString() ?? null;\n\tloadJobs().push(job);\n\tawait persist();\n\treturn job;\n}\n\n/** Look up a job by id, then by unique name. Throws when not found or when the name is ambiguous. */\nexport function getJob(idOrName: string): CronJob {\n\tconst jobs = loadJobs();\n\tconst byId = jobs.find((j) => j.id === idOrName);\n\tif (byId) return byId;\n\tconst byName = jobs.filter((j) => j.name === idOrName);\n\tif (byName.length === 1) return byName[0];\n\tif (byName.length > 1) {\n\t\tthrow new Error(\n\t\t\t`cron job name \"${idOrName}\" is ambiguous (${byName.length} matches: ${byName.map((j) => j.id).join(\", \")})`,\n\t\t);\n\t}\n\tthrow new Error(`no cron job matches \"${idOrName}\"`);\n}\n\nexport function listJobs(): CronJob[] {\n\treturn [...loadJobs()];\n}\n\nexport async function updateJob(idOrName: string, patch: JobPatch): Promise<CronJob> {\n\tconst job = getJob(idOrName);\n\tif (patch.name !== undefined) job.name = patch.name.trim().slice(0, MAX_NAME_LEN) || job.name;\n\tif (patch.workdir !== undefined) {\n\t\tif (patch.workdir) {\n\t\t\tconst err = validateWorkdir(patch.workdir);\n\t\t\tif (err) throw new Error(`updateJob: ${err}`);\n\t\t}\n\t\tjob.workdir = patch.workdir;\n\t}\n\tif (patch.deliver !== undefined) {\n\t\tconst err = runDeliverValidation(patch.deliver, patch.origin ?? job.origin);\n\t\tif (err) throw new Error(`updateJob: ${err}`);\n\t\tjob.deliver = patch.deliver.trim() || \"local\";\n\t}\n\tif (patch.prompt !== undefined) {\n\t\tconst prompt = patch.prompt.trim();\n\t\tif (!prompt) throw new Error(\"updateJob: prompt is empty\");\n\t\tjob.prompt = prompt;\n\t}\n\tif (patch.schedule !== undefined) {\n\t\tconst parsed = parseSchedule(patch.schedule);\n\t\tjob.schedule = parsed.schedule;\n\t\tjob.scheduleDisplay = parsed.scheduleDisplay;\n\t\tif (parsed.schedule.kind === \"once\") job.repeat.times = 1;\n\t\tjob.nextRunAt = computeNextRun(job)?.toISOString() ?? null;\n\t}\n\tif (patch.enabled !== undefined) job.enabled = patch.enabled;\n\tif (patch.times !== undefined) {\n\t\tif (patch.times !== null && (!Number.isInteger(patch.times) || patch.times < 1)) {\n\t\t\tthrow new Error(`updateJob: times must be a positive integer or null, got ${patch.times}`);\n\t\t}\n\t\tif (job.schedule.kind !== \"once\") job.repeat.times = patch.times;\n\t}\n\tif (patch.contextFrom !== undefined) job.contextFrom = patch.contextFrom;\n\tif (patch.origin !== undefined) job.origin = patch.origin;\n\tif (patch.nextRunAt !== undefined) job.nextRunAt = patch.nextRunAt;\n\tif (patch.lastDeliveryError !== undefined) job.lastDeliveryError = patch.lastDeliveryError;\n\tawait persist();\n\treturn job;\n}\n\nexport async function pauseJob(idOrName: string): Promise<CronJob> {\n\tconst job = getJob(idOrName);\n\tif (job.state !== \"completed\") job.state = \"paused\";\n\tawait persist();\n\treturn job;\n}\n\nexport async function resumeJob(idOrName: string): Promise<CronJob> {\n\tconst job = getJob(idOrName);\n\tif (job.state === \"paused\") {\n\t\tjob.state = \"scheduled\";\n\t\tjob.nextRunAt = computeNextRun(job)?.toISOString() ?? null;\n\t}\n\tawait persist();\n\treturn job;\n}\n\nexport async function removeJob(idOrName: string): Promise<CronJob> {\n\tconst job = getJob(idOrName);\n\tconst jobs = loadJobs();\n\tjobs.splice(jobs.indexOf(job), 1);\n\tawait persist();\n\treturn job;\n}\n\n// ---------------------------------------------------------------------------\n// Scheduling\n// ---------------------------------------------------------------------------\n\n/**\n * Jobs due to fire now. Skips disabled/paused/completed jobs and jobs without a\n * nextRunAt. One-shots fire up to 120s late (flat recovery grace); beyond that\n * they are marked state \"error\" and skipped. Recurring jobs whose nextRunAt is\n * older than the grace window — min(max(period/2, 120s), 2h) — are fast-forwarded\n * to their next future slot but still fire ONCE now (collapse backlog, no burst).\n */\nexport async function getDueJobs(now: Date = new Date()): Promise<CronJob[]> {\n\tconst due: CronJob[] = [];\n\tlet changed = false;\n\tfor (const job of loadJobs()) {\n\t\tif (!job.enabled || job.state === \"paused\" || job.state === \"completed\") continue;\n\t\tif (!job.nextRunAt) continue;\n\t\tconst next = new Date(job.nextRunAt).getTime();\n\t\tif (Number.isNaN(next) || now.getTime() < next) continue;\n\n\t\tif (job.schedule.kind === \"once\") {\n\t\t\tif (now.getTime() - next <= ONE_SHOT_GRACE_MS) {\n\t\t\t\tdue.push(job);\n\t\t\t} else {\n\t\t\t\tjob.state = \"error\";\n\t\t\t\tjob.lastStatus = \"error\";\n\t\t\t\tjob.lastError = \"missed one-shot schedule beyond the 120s recovery grace\";\n\t\t\t\tjob.nextRunAt = null;\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst grace = Math.min(Math.max(periodMs(job, now) / 2, MIN_GRACE_MS), MAX_GRACE_MS);\n\t\tif (now.getTime() - next > grace) {\n\t\t\tjob.nextRunAt = nextSlotAfter(job, now)?.toISOString() ?? null;\n\t\t\tchanged = true;\n\t\t}\n\t\tdue.push(job);\n\t}\n\tif (changed) await persist();\n\treturn due;\n}\n\n/**\n * Advance a recurring job to its next slot. Called by the scheduler BEFORE\n * execution so a crash mid-run loses at most that run (at-most-once).\n */\nexport async function advanceNextRun(jobId: string): Promise<CronJob> {\n\tconst job = getJob(jobId);\n\tif (job.schedule.kind !== \"once\") {\n\t\t// Advance to the first slot strictly after NOW: the job fires once for the\n\t\t// current slot, and the next fire is the next future slot (no catch-up burst).\n\t\tjob.nextRunAt = nextSlotAfter(job, new Date())?.toISOString() ?? computeNextRun(job)?.toISOString() ?? null;\n\t\tawait persist();\n\t}\n\treturn job;\n}\n\n/**\n * Record a finished run: lastRunAt/lastStatus/lastError, repeat.completed++,\n * then either complete the job (finite repeats exhausted, one-shots included)\n * or re-anchor nextRunAt off the previous scheduled slot (no drift).\n */\nexport async function markJobRun(jobId: string, result: { status: CronJobStatus; error?: string }): Promise<CronJob> {\n\tconst job = getJob(jobId);\n\tconst now = new Date();\n\tjob.lastRunAt = now.toISOString();\n\tjob.lastStatus = result.status;\n\tjob.lastError = result.error ?? null;\n\tjob.repeat.completed += 1;\n\tif (job.repeat.times !== null && job.repeat.completed >= job.repeat.times) {\n\t\tjob.state = \"completed\";\n\t\tjob.nextRunAt = null;\n\t} else if (job.schedule.kind === \"interval\") {\n\t\t// Anchor to the previous scheduled slot + period to avoid cumulative drift.\n\t\t// If advanceNextRun already moved nextRunAt into the future (scheduler path),\n\t\t// keep it; otherwise (manual run) step forward from now.\n\t\tconst current = job.nextRunAt ? new Date(job.nextRunAt) : now;\n\t\tif (current.getTime() > now.getTime()) {\n\t\t\t// Already advanced by the scheduler — keep the scheduled slot.\n\t\t\tjob.nextRunAt = current.toISOString();\n\t\t} else {\n\t\t\tjob.nextRunAt = nextSlotAfter(job, now)?.toISOString() ?? null;\n\t\t}\n\t} else if (job.schedule.kind === \"cron\") {\n\t\tjob.nextRunAt = new Cron(job.schedule.expr!).nextRun(now)?.toISOString() ?? null;\n\t}\n\tawait persist();\n\treturn job;\n}\n\n// ---------------------------------------------------------------------------\n// Per-run output\n// ---------------------------------------------------------------------------\n\nfunction formatStamp(d: Date): string {\n\tconst p = (n: number) => String(n).padStart(2, \"0\");\n\treturn `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;\n}\n\nfunction parseStamp(stamp: string): Date {\n\tconst m = /^(\\d{4})-(\\d{2})-(\\d{2})_(\\d{2})-(\\d{2})-(\\d{2})/.exec(stamp);\n\tif (!m) return new Date();\n\treturn new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), Number(m[6]));\n}\n\nlet lastOutputStamp = \"\";\n\n/** Monotonic timestamp stamp: never collides within the process, keeps lexicographic = chronological order. */\nfunction nextOutputStamp(): string {\n\tlet d = new Date();\n\tlet stamp = formatStamp(d);\n\tif (stamp <= lastOutputStamp) {\n\t\td = new Date(parseStamp(lastOutputStamp).getTime() + 1000);\n\t\tstamp = formatStamp(d);\n\t}\n\tlastOutputStamp = stamp;\n\treturn stamp;\n}\n\nfunction listOutputFiles(jobId: string): string[] {\n\tconst dir = outputDir(jobId);\n\tif (!existsSync(dir)) return [];\n\treturn readdirSync(dir)\n\t\t.filter((f) => f.endsWith(\".md\"))\n\t\t.sort();\n}\n\n/** Save a run's output and prune to the newest OUTPUT_RETENTION files. Returns the file path. */\nexport async function saveJobOutput(jobId: string, content: string): Promise<string> {\n\tconst dir = outputDir(jobId);\n\tmkdirSync(dir, { recursive: true });\n\tconst file = join(dir, `${nextOutputStamp()}.md`);\n\twriteFileSync(file, content, \"utf-8\");\n\tconst files = listOutputFiles(jobId);\n\twhile (files.length > OUTPUT_RETENTION) {\n\t\tconst oldest = files.shift();\n\t\tif (oldest) rmSync(join(dir, oldest), { force: true });\n\t}\n\treturn file;\n}\n\n/** Content of the newest output file for a job, or null when none exists. */\nexport function getLatestJobOutput(jobId: string): string | null {\n\tconst files = listOutputFiles(jobId);\n\tconst latest = files[files.length - 1];\n\tif (!latest) return null;\n\ttry {\n\t\treturn readFileSync(join(outputDir(jobId), latest), \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../../src/core/cron/jobs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * lunR: cron job model + persistent store (Phase 1 of the cron/gateway roadmap).\n *\n * Jobs persist at `<agentDir>/cron/jobs.json` (`{ jobs: [...], updated_at }`),\n * agentDir resolved via getAgentDir() (honors PI_CODING_AGENT_DIR, defaults\n * ~/.lunr/agent). Writes are atomic (tmp file in the same dir + rename) and\n * serialized with a cross-process read-modify-write lock over fresh data.\n *\n * Per-run output goes to `<agentDir>/cron/output/<jobId>/<YYYY-MM-DD_HH-MM-SS>.md`\n * with a retention cap of the newest 50 files per job.\n *\n * Tests inject a base dir via setCronBaseDir() so no real agent dir is touched.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport {\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\trealpathSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join, normalize, relative, resolve, sep } from \"node:path\";\nimport { Cron } from \"croner\";\nimport lockfile from \"proper-lockfile\";\nimport { getAgentDir } from \"../../config.ts\";\nimport { loadGatewayConfig } from \"../../gateway/config.ts\";\nimport {\n\tchatPlatformDeliverBlockedMessage,\n\tdeliverMentionsChatPlatform,\n\tisFeatureEnabled,\n} from \"../install-features.ts\";\n\n// ---------------------------------------------------------------------------\n// Model\n// ---------------------------------------------------------------------------\n\nexport type CronScheduleKind = \"once\" | \"interval\" | \"cron\";\n\nexport interface CronSchedule {\n\tkind: CronScheduleKind;\n\t/** ISO timestamp, one-shots only. */\n\trunAt?: string;\n\t/** Interval length in minutes, interval kind only. */\n\tminutes?: number;\n\t/** 5-field cron expression, cron kind only. */\n\texpr?: string;\n}\n\nexport interface CronJobOrigin {\n\t/** Requester identity, absent on legacy jobs. Never persist adapter role grants. */\n\tuserId?: string;\n\tplatform: string;\n\tchatId: string;\n\tthreadId?: string;\n\tchatType?: string;\n}\n\nexport type CronJobState = \"scheduled\" | \"paused\" | \"completed\" | \"error\";\nexport type CronJobStatus = \"ok\" | \"error\";\n\nexport interface CronJob {\n\tid: string;\n\tname: string;\n\tprompt: string;\n\tschedule: CronSchedule;\n\tscheduleDisplay: string;\n\trepeat: { times: number | null; completed: number };\n\tenabled: boolean;\n\tstate: CronJobState;\n\tcreatedAt: string;\n\tnextRunAt: string | null;\n\tlastRunAt: string | null;\n\tlastStatus: CronJobStatus | null;\n\tlastError: string | null;\n\tlastDeliveryError: string | null;\n\t/** \"local\" | \"origin\" | \"telegram[:chatId]\" | \"discord[:chatId]\" (comma-combinable). v1 TUI only uses \"local\". */\n\tdeliver: string;\n\torigin?: CronJobOrigin | null;\n\t/** Job IDs whose latest output (8K chars cap each) is prepended to the prompt. */\n\tcontextFrom?: string[];\n\tworkdir?: string;\n\t/** Unknown interrupted effects require an explicit run, rather than ordinary resume. */\n\tinterrupted?: boolean;\n\t/** Durable claim written before dispatch. Interrupted claims require explicit retry. */\n\tactiveRun?: { id: string; scheduledAt: string | null; startedAt: string; advancedTo?: string | null };\n}\n\nexport interface CreateJobInput {\n\tprompt: string;\n\t/** Raw schedule input, parsed by parseSchedule(). */\n\tschedule: string;\n\tname?: string;\n\t/** Repetition cap; null = forever. One-shots are always times=1. */\n\ttimes?: number | null;\n\tdeliver?: string;\n\tcontextFrom?: string[];\n\tworkdir?: string;\n\torigin?: CronJobOrigin | null;\n}\n\nexport interface JobPatch {\n\tname?: string;\n\tprompt?: string;\n\t/** Raw schedule input; reparsed and re-anchors nextRunAt. */\n\tschedule?: string;\n\tdeliver?: string;\n\tenabled?: boolean;\n\ttimes?: number | null;\n\tcontextFrom?: string[];\n\tworkdir?: string;\n\torigin?: CronJobOrigin | null;\n\t/** Internal: manual-run trigger and delivery-error bookkeeping. */\n\tnextRunAt?: string | null;\n\tlastDeliveryError?: string | null;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Flat recovery grace for one-shots: fire up to 120s late. */\nexport const ONE_SHOT_GRACE_MS = 120_000;\n/** Per-job output retention. */\nexport const OUTPUT_RETENTION = 50;\n/** Cap per upstream job output injected via contextFrom. */\nexport const CONTEXT_OUTPUT_CAP = 8000;\n\nconst MAX_NAME_LEN = 50;\n\n// ---------------------------------------------------------------------------\n// Paths + store state\n// ---------------------------------------------------------------------------\n\nlet baseDirOverride: string | undefined;\nlet transactionJobs: CronJob[] | undefined;\nlet writeQueue: Promise<void> = Promise.resolve();\n\n/**\n * Override the base dir (defaults to the agent dir) and reset the in-memory\n * transaction queue. Passing undefined restores the default. For tests.\n */\nexport function setCronBaseDir(dir: string | undefined): void {\n\tbaseDirOverride = dir;\n\ttransactionJobs = undefined;\n\twriteQueue = Promise.resolve();\n}\n\nfunction cronDir(): string {\n\treturn join(baseDirOverride ?? getAgentDir(), \"cron\");\n}\n\nfunction jobsFile(): string {\n\treturn join(cronDir(), \"jobs.json\");\n}\n\nfunction outputDir(jobId: string): string {\n\treturn join(cronDir(), \"output\", jobId);\n}\n\nfunction loadJobs(): CronJob[] {\n\tif (transactionJobs) return transactionJobs;\n\tconst file = jobsFile();\n\tif (!existsSync(file)) return [];\n\tconst parsed = JSON.parse(readFileSync(file, \"utf-8\"));\n\tif (!Array.isArray(parsed?.jobs)) throw new Error(\"invalid cron job store\");\n\treturn parsed.jobs as CronJob[];\n}\n\n/** Each mutation reads the latest snapshot while holding the profile store lock. */\nfunction mutateJobs<T>(fn: () => T): Promise<T> {\n\tconst dir = cronDir();\n\tconst file = join(dir, \"jobs.json\");\n\tconst operation = writeQueue.then(async () => {\n\t\tmkdirSync(dir, { recursive: true });\n\t\tconst release = await lockfile.lock(file, {\n\t\t\trealpath: false,\n\t\t\tretries: { retries: 100, minTimeout: 10, maxTimeout: 100 },\n\t\t});\n\t\ttry {\n\t\t\ttransactionJobs = loadJobs();\n\t\t\tconst result = fn();\n\t\t\tconst tmp = join(dir, `.jobs.json.${randomUUID()}.tmp`);\n\t\t\ttry {\n\t\t\t\twriteFileSync(\n\t\t\t\t\ttmp,\n\t\t\t\t\tJSON.stringify({ jobs: transactionJobs, updated_at: new Date().toISOString() }, null, 2),\n\t\t\t\t);\n\t\t\t\trenameSync(tmp, file);\n\t\t\t} finally {\n\t\t\t\trmSync(tmp, { force: true });\n\t\t\t}\n\t\t\treturn structuredClone(result);\n\t\t} finally {\n\t\t\ttransactionJobs = undefined;\n\t\t\tawait release();\n\t\t}\n\t});\n\twriteQueue = operation.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn operation;\n}\n\n/** Acquire the non-preemptive profile scheduler lease. A live PID is never displaced. */\nexport async function acquireSchedulerLease(): Promise<(() => Promise<void>) | null> {\n\tconst dir = cronDir();\n\tmkdirSync(dir, { recursive: true });\n\tconst file = join(dir, \"scheduler\");\n\tconst ownerFile = `${file}.owner.json`;\n\tconst staleMs = 120_000;\n\tif (existsSync(`${file}.lock`)) {\n\t\tlet owner: unknown;\n\t\ttry {\n\t\t\towner = JSON.parse(readFileSync(ownerFile, \"utf-8\"));\n\t\t\tif (\n\t\t\t\t!owner ||\n\t\t\t\ttypeof owner !== \"object\" ||\n\t\t\t\t!(\"pid\" in owner) ||\n\t\t\t\ttypeof owner.pid !== \"number\" ||\n\t\t\t\t!Number.isInteger(owner.pid) ||\n\t\t\t\towner.pid <= 0 ||\n\t\t\t\towner.pid > 2_147_483_647\n\t\t\t)\n\t\t\t\tthrow new Error(\"invalid scheduler owner PID\");\n\t\t} catch (error) {\n\t\t\t// A competing operator may still be publishing its owner record. Missing\n\t\t\t// or malformed metadata never proves that an existing owner is dead.\n\t\t\tif (!existsSync(`${file}.lock`)) return acquireSchedulerLease();\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\ttry {\n\t\t\t\t\tif (Date.now() - statSync(`${file}.lock`).mtimeMs < staleMs) return null;\n\t\t\t\t} catch (lockError) {\n\t\t\t\t\tif ((lockError as NodeJS.ErrnoException).code === \"ENOENT\") return acquireSchedulerLease();\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow new Error(\n\t\t\t\t`Cron scheduler ownership cannot be verified. Before repairing it, confirm every operator using this profile has stopped. ` +\n\t\t\t\t\t`Then remove ${file}.lock and ${ownerFile}, and restart scheduling. Never remove them while an operator may be running.`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\t\ttry {\n\t\t\tprocess.kill(owner.pid, 0);\n\t\t\treturn null;\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ESRCH\") return null;\n\t\t}\n\t}\n\tlet release: () => Promise<void>;\n\ttry {\n\t\trelease = await lockfile.lock(file, { realpath: false, stale: staleMs });\n\t} catch (error) {\n\t\tif ((error as NodeJS.ErrnoException).code === \"ELOCKED\") return null;\n\t\tthrow error;\n\t}\n\tconst ownerTmp = join(dir, `.scheduler.owner.json.${randomUUID()}.tmp`);\n\ttry {\n\t\twriteFileSync(ownerTmp, JSON.stringify({ pid: process.pid }));\n\t\trenameSync(ownerTmp, ownerFile);\n\t} catch (error) {\n\t\tawait release();\n\t\tthrow error;\n\t} finally {\n\t\trmSync(ownerTmp, { force: true });\n\t}\n\treturn async () => {\n\t\ttry {\n\t\t\trmSync(ownerFile, { force: true });\n\t\t} finally {\n\t\t\tawait release();\n\t\t}\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Schedule parsing\n// ---------------------------------------------------------------------------\n\nexport interface ParsedSchedule {\n\tschedule: CronSchedule;\n\tscheduleDisplay: string;\n}\n\nconst INTERVAL_RE = /^every\\s+(\\d+)\\s*([mhd])$/i;\nconst DURATION_RE = /^(\\d+)\\s*([mhd])$/i;\n\nfunction toMinutes(value: number, unit: string): number {\n\tconst u = unit.toLowerCase();\n\tconst factor = u === \"d\" ? 1440 : u === \"h\" ? 60 : 1;\n\treturn value * factor;\n}\n\n/**\n * Parse a schedule input:\n * - `every 30m` / `every 2h` / `every 1d` → interval (minutes; d = 1440)\n * - bare `30m` / `2h` / `1d` → one-shot at now + duration\n * - ISO timestamp → one-shot at that time\n * - 5-field cron expression (validated via croner; throws on invalid)\n */\nexport function parseSchedule(input: string, now: Date = new Date()): ParsedSchedule {\n\tconst text = input.trim().replace(/\\s+/g, \" \");\n\tif (!text) throw new Error(\"schedule is empty\");\n\n\tconst interval = INTERVAL_RE.exec(text);\n\tif (interval) {\n\t\tconst minutes = toMinutes(Number(interval[1]), interval[2]);\n\t\tif (minutes <= 0) throw new Error(`invalid interval: \"${text}\"`);\n\t\treturn {\n\t\t\tschedule: { kind: \"interval\", minutes },\n\t\t\tscheduleDisplay: `every ${Number(interval[1])}${interval[2].toLowerCase()}`,\n\t\t};\n\t}\n\n\tconst duration = DURATION_RE.exec(text);\n\tif (duration) {\n\t\tconst minutes = toMinutes(Number(duration[1]), duration[2]);\n\t\tif (minutes <= 0) throw new Error(`invalid duration: \"${text}\"`);\n\t\tconst runAt = new Date(now.getTime() + minutes * 60_000).toISOString();\n\t\treturn { schedule: { kind: \"once\", runAt }, scheduleDisplay: text };\n\t}\n\n\tif (/^\\d{4}-\\d{2}-\\d{2}/.test(text)) {\n\t\tconst ts = Date.parse(text);\n\t\tif (!Number.isNaN(ts)) {\n\t\t\tconst runAt = new Date(ts).toISOString();\n\t\t\treturn { schedule: { kind: \"once\", runAt }, scheduleDisplay: runAt };\n\t\t}\n\t}\n\n\tif (text.split(\" \").length === 5) {\n\t\ttry {\n\t\t\tnew Cron(text);\n\t\t} catch (err) {\n\t\t\tthrow new Error(`invalid cron expression \"${text}\": ${err instanceof Error ? err.message : String(err)}`);\n\t\t}\n\t\treturn { schedule: { kind: \"cron\", expr: text }, scheduleDisplay: text };\n\t}\n\n\tthrow new Error(\n\t\t`unrecognized schedule: \"${input}\" (expected \"every 30m\", \"2h\", an ISO timestamp, or a 5-field cron expression)`,\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Next-run computation\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the next run time for a job:\n * - once: the stored runAt, recoverable within a 120s grace if never run; null after that or once run.\n * - interval: (lastRunAt ?? createdAt) + minutes.\n * - cron: croner nextRun after (lastRunAt ?? now).\n */\nexport function computeNextRun(job: CronJob, now: Date = new Date()): Date | null {\n\tconst s = job.schedule;\n\tif (s.kind === \"once\") {\n\t\tif (job.repeat.completed > 0) return null;\n\t\tif (!s.runAt) return null;\n\t\tconst runAt = new Date(s.runAt);\n\t\tif (Number.isNaN(runAt.getTime())) return null;\n\t\treturn now.getTime() - runAt.getTime() <= ONE_SHOT_GRACE_MS ? runAt : null;\n\t}\n\tif (s.kind === \"interval\") {\n\t\tconst base = job.lastRunAt ? new Date(job.lastRunAt) : new Date(job.createdAt);\n\t\treturn new Date(base.getTime() + (s.minutes ?? 0) * 60_000);\n\t}\n\tconst cron = new Cron(s.expr!);\n\treturn cron.nextRun(job.lastRunAt ? new Date(job.lastRunAt) : now);\n}\n\n/** Next slot strictly after `after` for a recurring job. */\nfunction nextSlotAfter(job: CronJob, after: Date): Date | null {\n\tif (job.schedule.kind === \"interval\") {\n\t\tconst period = (job.schedule.minutes ?? 0) * 60_000;\n\t\tif (period <= 0) return null;\n\t\t// Step forward from the stored nextRunAt (or now) until strictly in the future of `after`.\n\t\tlet slot = job.nextRunAt ? new Date(job.nextRunAt).getTime() : new Date(job.createdAt).getTime() + period;\n\t\twhile (slot <= after.getTime()) slot += period;\n\t\treturn new Date(slot);\n\t}\n\tif (job.schedule.kind === \"cron\") {\n\t\treturn new Cron(job.schedule.expr!).nextRun(after);\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nlet workdirRoots: string[] = [process.cwd(), homedir()];\nlet deliverValidator: ((deliver: string, origin?: CronJobOrigin | null) => string | undefined) | undefined;\n\n/**\n * Configure the allowed absolute roots for cron job workdirs. Relative workdirs\n * are resolved against process.cwd() and must fall under one of these roots.\n * Default roots are process.cwd() and the user's home directory.\n */\nexport function setCronWorkdirRoots(roots: string[]): void {\n\tworkdirRoots = roots.map((r) => normalize(resolve(r)));\n}\n\n/**\n * Configure a deliver-target validator. When set, createJob/updateJob reject\n * deliver strings that the validator does not approve. The gateway uses this\n * to prevent delivery to arbitrary chats.\n */\nexport function setCronDeliverValidator(\n\tfn: ((deliver: string, origin?: CronJobOrigin | null) => string | undefined) | undefined,\n): void {\n\tdeliverValidator = fn;\n}\n\n/** Test seam: restore default validation state. */\nexport function resetCronValidators(): void {\n\tworkdirRoots = [process.cwd(), homedir()];\n\tdeliverValidator = undefined;\n}\n\nfunction isPathUnderRoot(absPath: string, root: string): boolean {\n\tconst rel = relative(root, absPath);\n\treturn rel === \"\" || (!rel.startsWith(`..${sep}`) && rel !== \"..\" && !isAbsolute(rel));\n}\n\nfunction validateWorkdir(workdir: string, origin?: CronJobOrigin | null): string | undefined {\n\tconst candidate = origin ? realpathSync(resolve(workdir)) : resolve(workdir);\n\tconst roots = origin\n\t\t? (loadGatewayConfig().projectRoots ?? []).flatMap((root) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn [realpathSync(root)];\n\t\t\t\t} catch {\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t})\n\t\t: workdirRoots;\n\tif (isAbsolute(workdir) && !roots.some((root) => isPathUnderRoot(candidate, root))) {\n\t\treturn `workdir ${workdir} is outside allowed roots`;\n\t}\n\tif (!roots.some((root) => isPathUnderRoot(candidate, root))) {\n\t\treturn `workdir ${workdir} resolves outside allowed roots`;\n\t}\n\treturn undefined;\n}\n\nfunction runDeliverValidation(deliver: string, origin?: CronJobOrigin | null): string | undefined {\n\tif (deliverMentionsChatPlatform(deliver) && !isFeatureEnabled(\"chat-platforms\")) {\n\t\treturn chatPlatformDeliverBlockedMessage();\n\t}\n\treturn deliverValidator?.(deliver, origin);\n}\n\nfunction deriveName(prompt: string): string {\n\tconst first = prompt.split(\"\\n\")[0].replace(/\\s+/g, \" \").trim();\n\treturn (first || \"cron job\").slice(0, MAX_NAME_LEN);\n}\n\nexport async function createJob(input: CreateJobInput): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst prompt = input.prompt.trim();\n\t\tif (!prompt) throw new Error(\"createJob: prompt is empty\");\n\t\tif (input.workdir) {\n\t\t\tconst err = validateWorkdir(input.workdir, input.origin);\n\t\t\tif (err) throw new Error(`createJob: ${err}`);\n\t\t}\n\t\tif (input.deliver) {\n\t\t\tconst err = runDeliverValidation(input.deliver, input.origin);\n\t\t\tif (err) throw new Error(`createJob: ${err}`);\n\t\t}\n\t\tconst now = new Date();\n\t\tconst parsed = parseSchedule(input.schedule, now);\n\t\tconst isOneShot = parsed.schedule.kind === \"once\";\n\t\tconst times = isOneShot ? 1 : (input.times ?? null);\n\t\tif (times !== null && (!Number.isInteger(times) || times < 1)) {\n\t\t\tthrow new Error(`createJob: times must be a positive integer or null, got ${times}`);\n\t\t}\n\t\tconst job: CronJob = {\n\t\t\tid: randomUUID().replaceAll(\"-\", \"\").slice(0, 12),\n\t\t\tname: (input.name?.trim() || deriveName(prompt)).slice(0, MAX_NAME_LEN),\n\t\t\tprompt,\n\t\t\tschedule: parsed.schedule,\n\t\t\tscheduleDisplay: parsed.scheduleDisplay,\n\t\t\trepeat: { times, completed: 0 },\n\t\t\tenabled: true,\n\t\t\tstate: \"scheduled\",\n\t\t\tcreatedAt: now.toISOString(),\n\t\t\tnextRunAt: null,\n\t\t\tlastRunAt: null,\n\t\t\tlastStatus: null,\n\t\t\tlastError: null,\n\t\t\tlastDeliveryError: null,\n\t\t\tdeliver: input.deliver?.trim() || \"local\",\n\t\t\torigin: input.origin ?? null,\n\t\t\tcontextFrom: input.contextFrom,\n\t\t\tworkdir: input.workdir,\n\t\t};\n\t\tjob.nextRunAt = computeNextRun(job, now)?.toISOString() ?? null;\n\t\tloadJobs().push(job);\n\t\treturn job;\n\t});\n}\n\n/** Look up a job by id, then by unique name. Throws when not found or when the name is ambiguous. */\nexport function getJob(idOrName: string): CronJob {\n\tconst jobs = loadJobs();\n\tconst byId = jobs.find((j) => j.id === idOrName);\n\tif (byId) return byId;\n\tconst byName = jobs.filter((j) => j.name === idOrName);\n\tif (byName.length === 1) return byName[0];\n\tif (byName.length > 1) {\n\t\tthrow new Error(\n\t\t\t`cron job name \"${idOrName}\" is ambiguous (${byName.length} matches: ${byName.map((j) => j.id).join(\", \")})`,\n\t\t);\n\t}\n\tthrow new Error(`no cron job matches \"${idOrName}\"`);\n}\n\nexport function listJobs(): CronJob[] {\n\treturn [...loadJobs()];\n}\n\nexport async function updateJob(idOrName: string, patch: JobPatch): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst job = getJob(idOrName);\n\t\tif (patch.name !== undefined) job.name = patch.name.trim().slice(0, MAX_NAME_LEN) || job.name;\n\t\tif (patch.workdir !== undefined) {\n\t\t\tif (patch.workdir) {\n\t\t\t\tconst err = validateWorkdir(patch.workdir, patch.origin ?? job.origin);\n\t\t\t\tif (err) throw new Error(`updateJob: ${err}`);\n\t\t\t}\n\t\t\tjob.workdir = patch.workdir;\n\t\t}\n\t\tif (patch.deliver !== undefined) {\n\t\t\tconst err = runDeliverValidation(patch.deliver, patch.origin ?? job.origin);\n\t\t\tif (err) throw new Error(`updateJob: ${err}`);\n\t\t\tjob.deliver = patch.deliver.trim() || \"local\";\n\t\t}\n\t\tif (patch.prompt !== undefined) {\n\t\t\tconst prompt = patch.prompt.trim();\n\t\t\tif (!prompt) throw new Error(\"updateJob: prompt is empty\");\n\t\t\tjob.prompt = prompt;\n\t\t}\n\t\tif (patch.schedule !== undefined) {\n\t\t\tconst parsed = parseSchedule(patch.schedule);\n\t\t\tjob.schedule = parsed.schedule;\n\t\t\tjob.scheduleDisplay = parsed.scheduleDisplay;\n\t\t\tif (parsed.schedule.kind === \"once\") job.repeat.times = 1;\n\t\t\tjob.nextRunAt = computeNextRun(job)?.toISOString() ?? null;\n\t\t}\n\t\tif (patch.enabled !== undefined) job.enabled = patch.enabled;\n\t\tif (patch.times !== undefined) {\n\t\t\tif (patch.times !== null && (!Number.isInteger(patch.times) || patch.times < 1)) {\n\t\t\t\tthrow new Error(`updateJob: times must be a positive integer or null, got ${patch.times}`);\n\t\t\t}\n\t\t\tif (job.schedule.kind !== \"once\") job.repeat.times = patch.times;\n\t\t}\n\t\tif (patch.contextFrom !== undefined) job.contextFrom = patch.contextFrom;\n\t\tif (patch.origin !== undefined) job.origin = patch.origin;\n\t\tif (patch.nextRunAt !== undefined) job.nextRunAt = patch.nextRunAt;\n\t\tif (patch.lastDeliveryError !== undefined) job.lastDeliveryError = patch.lastDeliveryError;\n\t\treturn job;\n\t});\n}\n\nexport async function pauseJob(idOrName: string): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst job = getJob(idOrName);\n\t\tif (job.state !== \"completed\") job.state = \"paused\";\n\t\treturn job;\n\t});\n}\n\nexport async function resumeJob(idOrName: string): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst job = getJob(idOrName);\n\t\tif (job.interrupted)\n\t\t\tthrow new Error(\"interrupted run has unknown effects; inspect them and use cron run for a deliberate retry\");\n\t\tif (job.state === \"paused\") {\n\t\t\tjob.state = \"scheduled\";\n\t\t\tjob.nextRunAt = computeNextRun(job)?.toISOString() ?? null;\n\t\t}\n\t\treturn job;\n\t});\n}\n\nexport async function removeJob(idOrName: string): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst job = getJob(idOrName);\n\t\tconst jobs = loadJobs();\n\t\tjobs.splice(jobs.indexOf(job), 1);\n\t\treturn job;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Scheduling\n// ---------------------------------------------------------------------------\n\n/**\n * Jobs due to fire now. Skips disabled/paused/completed jobs and jobs without a\n * nextRunAt. One-shots fire up to 120s late (flat recovery grace); beyond that\n * they are marked state \"error\" and skipped. Recurring backlog fires once;\n * claiming the occurrence advances it to the next future slot without a burst.\n */\nexport async function getDueJobs(now: Date = new Date()): Promise<CronJob[]> {\n\treturn mutateJobs(() => {\n\t\tconst due: CronJob[] = [];\n\t\tfor (const job of loadJobs()) {\n\t\t\tif (!job.enabled || job.state !== \"scheduled\" || job.activeRun) continue;\n\t\t\tif (!job.nextRunAt) continue;\n\t\t\tconst next = new Date(job.nextRunAt).getTime();\n\t\t\tif (Number.isNaN(next) || now.getTime() < next) continue;\n\n\t\t\tif (job.schedule.kind === \"once\") {\n\t\t\t\tif (now.getTime() - next <= ONE_SHOT_GRACE_MS) {\n\t\t\t\t\tdue.push(job);\n\t\t\t\t} else {\n\t\t\t\t\tjob.state = \"error\";\n\t\t\t\t\tjob.lastStatus = \"error\";\n\t\t\t\t\tjob.lastError = \"missed one-shot schedule beyond the 120s recovery grace\";\n\t\t\t\t\tjob.nextRunAt = null;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdue.push(job);\n\t\t}\n\t\treturn due;\n\t});\n}\n\n/**\n * Advance a recurring job to its next slot. Scheduler dispatch uses claimJobRun\n * instead so one-shots and recurring work both retain a durable claim.\n */\nexport async function advanceNextRun(jobId: string): Promise<CronJob> {\n\treturn mutateJobs(() => {\n\t\tconst job = getJob(jobId);\n\t\tif (job.schedule.kind !== \"once\") {\n\t\t\t// Advance to the first slot strictly after NOW: the job fires once for the\n\t\t\t// current slot, and the next fire is the next future slot (no catch-up burst).\n\t\t\tjob.nextRunAt = nextSlotAfter(job, new Date())?.toISOString() ?? computeNextRun(job)?.toISOString() ?? null;\n\t\t}\n\t\treturn job;\n\t});\n}\n\n/**\n * Record a finished run: lastRunAt/lastStatus/lastError, repeat.completed++,\n * then either complete the job (finite repeats exhausted, one-shots included)\n * or re-anchor nextRunAt off the previous scheduled slot (no drift).\n */\nexport async function markJobRun(\n\tjobId: string,\n\tresult: { status: CronJobStatus; error?: string; runId?: string },\n): Promise<CronJob | null> {\n\treturn mutateJobs(() => {\n\t\tconst job = loadJobs().find((job) => job.id === jobId);\n\t\tif (!job || (result.runId && job.activeRun?.id !== result.runId)) return null;\n\t\tdelete job.activeRun;\n\t\tdelete job.interrupted;\n\t\tconst now = new Date();\n\t\tjob.lastRunAt = now.toISOString();\n\t\tjob.lastStatus = result.status;\n\t\tjob.lastError = result.error ?? null;\n\t\tjob.repeat.completed += 1;\n\t\tif (job.repeat.times !== null && job.repeat.completed >= job.repeat.times) {\n\t\t\tjob.state = \"completed\";\n\t\t\tjob.nextRunAt = null;\n\t\t} else if (!result.runId && job.schedule.kind === \"interval\") {\n\t\t\t// Anchor to the previous scheduled slot + period to avoid cumulative drift.\n\t\t\t// If advanceNextRun already moved nextRunAt into the future (scheduler path),\n\t\t\t// keep it; otherwise (manual run) step forward from now.\n\t\t\tconst current = job.nextRunAt ? new Date(job.nextRunAt) : now;\n\t\t\tif (current.getTime() > now.getTime()) {\n\t\t\t\t// Already advanced by the scheduler — keep the scheduled slot.\n\t\t\t\tjob.nextRunAt = current.toISOString();\n\t\t\t} else {\n\t\t\t\tjob.nextRunAt = nextSlotAfter(job, now)?.toISOString() ?? null;\n\t\t\t}\n\t\t} else if (!result.runId && job.schedule.kind === \"cron\") {\n\t\t\tjob.nextRunAt = new Cron(job.schedule.expr!).nextRun(now)?.toISOString() ?? null;\n\t\t}\n\t\treturn job;\n\t});\n}\n\n/** Claim only a still-due, enabled occurrence from the fresh store. */\nexport async function claimJobRun(jobId: string, now: Date, manual = false): Promise<CronJob | null> {\n\treturn mutateJobs(() => {\n\t\tconst job = loadJobs().find((job) => job.id === jobId);\n\t\tif (!job || job.activeRun) return null;\n\t\tif (!manual && (!job.enabled || job.state !== \"scheduled\" || !job.nextRunAt || new Date(job.nextRunAt) > now))\n\t\t\treturn null;\n\t\tjob.activeRun = { id: randomUUID(), scheduledAt: job.nextRunAt, startedAt: now.toISOString() };\n\t\tjob.nextRunAt = job.schedule.kind === \"once\" ? null : (nextSlotAfter(job, now)?.toISOString() ?? null);\n\t\tjob.activeRun.advancedTo = job.nextRunAt;\n\t\treturn job;\n\t});\n}\n\n/** Recover uncertain interrupted work without replaying its external effects. */\nexport async function recoverInterruptedRuns(): Promise<void> {\n\tawait mutateJobs(() => {\n\t\tfor (const job of loadJobs()) {\n\t\t\tif (!job.activeRun) continue;\n\t\t\tjob.state = \"paused\";\n\t\t\tjob.lastStatus = \"error\";\n\t\t\tjob.lastError = \"interrupted run has unknown effects; use cron run to retry deliberately\";\n\t\t\tjob.interrupted = true;\n\t\t\tdelete job.activeRun;\n\t\t}\n\t});\n}\n\nexport async function deferJobRun(jobId: string, runId: string): Promise<void> {\n\tawait mutateJobs(() => {\n\t\tconst job = loadJobs().find((job) => job.id === jobId);\n\t\tif (job?.activeRun?.id !== runId) return;\n\t\tif (job.nextRunAt === job.activeRun.advancedTo) job.nextRunAt = job.activeRun.scheduledAt;\n\t\tdelete job.activeRun;\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Per-run output\n// ---------------------------------------------------------------------------\n\nfunction formatStamp(d: Date): string {\n\tconst p = (n: number) => String(n).padStart(2, \"0\");\n\treturn `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}_${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;\n}\n\nfunction parseStamp(stamp: string): Date {\n\tconst m = /^(\\d{4})-(\\d{2})-(\\d{2})_(\\d{2})-(\\d{2})-(\\d{2})/.exec(stamp);\n\tif (!m) return new Date();\n\treturn new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4]), Number(m[5]), Number(m[6]));\n}\n\nlet lastOutputStamp = \"\";\n\n/** Monotonic timestamp stamp: never collides within the process, keeps lexicographic = chronological order. */\nfunction nextOutputStamp(): string {\n\tlet d = new Date();\n\tlet stamp = formatStamp(d);\n\tif (stamp <= lastOutputStamp) {\n\t\td = new Date(parseStamp(lastOutputStamp).getTime() + 1000);\n\t\tstamp = formatStamp(d);\n\t}\n\tlastOutputStamp = stamp;\n\treturn stamp;\n}\n\nfunction listOutputFiles(jobId: string): string[] {\n\tconst dir = outputDir(jobId);\n\tif (!existsSync(dir)) return [];\n\treturn readdirSync(dir)\n\t\t.filter((f) => f.endsWith(\".md\"))\n\t\t.sort();\n}\n\n/** Save a run's output and prune to the newest OUTPUT_RETENTION files. Returns the file path. */\nexport async function saveJobOutput(jobId: string, content: string): Promise<string> {\n\tconst dir = outputDir(jobId);\n\tmkdirSync(dir, { recursive: true });\n\tconst file = join(dir, `${nextOutputStamp()}.md`);\n\twriteFileSync(file, content, \"utf-8\");\n\tconst files = listOutputFiles(jobId);\n\twhile (files.length > OUTPUT_RETENTION) {\n\t\tconst oldest = files.shift();\n\t\tif (oldest) rmSync(join(dir, oldest), { force: true });\n\t}\n\treturn file;\n}\n\n/** Content of the newest output file for a job, or null when none exists. */\nexport function getLatestJobOutput(jobId: string): string | null {\n\tconst files = listOutputFiles(jobId);\n\tconst latest = files[files.length - 1];\n\tif (!latest) return null;\n\ttry {\n\t\treturn readFileSync(join(outputDir(jobId), latest), \"utf-8\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n"]}
|