pi-harness-runtime 1.1.397 → 1.1.402
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/AGENTS.md +44 -2
- package/CHANGELOG.md +21 -0
- package/RULES.md +28 -0
- package/harness/agent-handoff.ts +2 -3
- package/harness/autonomous-review.ts +1 -1
- package/harness/blackboard.ts +2 -3
- package/harness/context-compact-orchestrator.ts +1 -1
- package/harness/context-window-manager.ts +2 -2
- package/harness/e2e/playwright-runner.ts +6 -4
- package/harness/herdr-agents.ts +1 -1
- package/harness/job-state-machine.ts +1 -1
- package/harness/langchain/daemon.ts +3 -3
- package/harness/langchain/graph.ts +2 -2
- package/harness/langchain/ping-pong-middleware.ts +2 -2
- package/harness/loop-code-agent.ts +6 -4
- package/harness/loop-controller.ts +1 -1
- package/harness/loop-review-agent.ts +1 -1
- package/harness/loop-types.ts +1 -1
- package/harness/master-planner.ts +1 -1
- package/harness/notification-events.ts +6 -6
- package/harness/project-detector/detector.ts +1 -2
- package/harness/repair-engine.ts +2 -4
- package/harness/service-diagnostics.ts +1 -1
- package/harness/skill-sync.ts +3 -3
- package/index.ts +276 -55
- package/mirror.ts +1 -1
- package/package.json +5 -3
- package/packages/a2a-adapter/dist/discovery.d.ts.map +1 -1
- package/packages/a2a-adapter/dist/discovery.js +0 -1
- package/packages/a2a-adapter/dist/discovery.js.map +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/a2a-adapter/src/discovery.ts +0 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/dist/index.d.ts +9 -0
- package/packages/auth/dist/index.d.ts.map +1 -0
- package/packages/auth/dist/index.js +9 -0
- package/packages/auth/dist/index.js.map +1 -0
- package/packages/auth/package.json +1 -1
- package/packages/auth/src/index.ts +9 -0
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/dist/index.d.ts +52 -0
- package/packages/config-capture/dist/index.d.ts.map +1 -0
- package/packages/config-capture/dist/index.js +261 -0
- package/packages/config-capture/dist/index.js.map +1 -0
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/dist/extension.d.ts +15 -0
- package/packages/file-copy-helper/dist/extension.d.ts.map +1 -0
- package/packages/file-copy-helper/dist/extension.debug.d.ts +15 -0
- package/packages/file-copy-helper/dist/extension.debug.d.ts.map +1 -0
- package/packages/file-copy-helper/dist/extension.debug.js +215 -0
- package/packages/file-copy-helper/dist/extension.debug.js.map +1 -0
- package/packages/file-copy-helper/dist/extension.js +222 -0
- package/packages/file-copy-helper/dist/extension.js.map +1 -0
- package/packages/file-copy-helper/dist/index.d.ts +3 -0
- package/packages/file-copy-helper/dist/index.d.ts.map +1 -0
- package/packages/file-copy-helper/dist/index.js +3 -0
- package/packages/file-copy-helper/dist/index.js.map +1 -0
- package/packages/file-copy-helper/dist/intent-detector.d.ts +21 -0
- package/packages/file-copy-helper/dist/intent-detector.d.ts.map +1 -0
- package/packages/file-copy-helper/dist/intent-detector.js +348 -0
- package/packages/file-copy-helper/dist/intent-detector.js.map +1 -0
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/dist/auto-continue.d.ts +161 -2
- package/packages/jev-judge/dist/auto-continue.d.ts.map +1 -1
- package/packages/jev-judge/dist/auto-continue.js +267 -22
- package/packages/jev-judge/dist/auto-continue.js.map +1 -1
- package/packages/jev-judge/dist/index.d.ts +2 -0
- package/packages/jev-judge/dist/index.d.ts.map +1 -1
- package/packages/jev-judge/dist/index.js +2 -0
- package/packages/jev-judge/dist/index.js.map +1 -1
- package/packages/jev-judge/dist/question-risk.d.ts +50 -0
- package/packages/jev-judge/dist/question-risk.d.ts.map +1 -0
- package/packages/jev-judge/dist/question-risk.js +100 -0
- package/packages/jev-judge/dist/question-risk.js.map +1 -0
- package/packages/jev-judge/package.json +1 -1
- package/packages/jev-judge/src/auto-continue.ts +426 -29
- package/packages/jev-judge/src/index.ts +4 -0
- package/packages/jev-judge/src/question-risk.ts +151 -0
- package/packages/jev-judge/tests/auto-continue-context.test.ts +221 -0
- package/packages/jev-judge/tests/outcome-application.test.ts +132 -0
- package/packages/jev-judge/tests/question-risk.test.ts +116 -0
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/logging/README.md +36 -3
- package/packages/logging/dist/config.d.ts +7 -0
- package/packages/logging/dist/config.d.ts.map +1 -1
- package/packages/logging/dist/config.js +14 -0
- package/packages/logging/dist/config.js.map +1 -1
- package/packages/logging/dist/incidents/schema.d.ts +111 -0
- package/packages/logging/dist/incidents/schema.d.ts.map +1 -0
- package/packages/logging/dist/incidents/schema.js +0 -0
- package/packages/logging/dist/incidents/schema.js.map +1 -0
- package/packages/logging/dist/index.d.ts +2 -0
- package/packages/logging/dist/index.d.ts.map +1 -1
- package/packages/logging/dist/index.js +2 -0
- package/packages/logging/dist/index.js.map +1 -1
- package/packages/logging/dist/server/http.d.ts +4 -3
- package/packages/logging/dist/server/http.d.ts.map +1 -1
- package/packages/logging/dist/server/http.js +35 -3
- package/packages/logging/dist/server/http.js.map +1 -1
- package/packages/logging/dist/server/incident-qdrant.d.ts +83 -0
- package/packages/logging/dist/server/incident-qdrant.d.ts.map +1 -0
- package/packages/logging/dist/server/incident-qdrant.js +129 -0
- package/packages/logging/dist/server/incident-qdrant.js.map +1 -0
- package/packages/logging/dist/server/incident-store.d.ts +76 -0
- package/packages/logging/dist/server/incident-store.d.ts.map +1 -0
- package/packages/logging/dist/server/incident-store.js +157 -0
- package/packages/logging/dist/server/incident-store.js.map +1 -0
- package/packages/logging/dist/server/index.d.ts +4 -0
- package/packages/logging/dist/server/index.d.ts.map +1 -1
- package/packages/logging/dist/server/index.js +2 -0
- package/packages/logging/dist/server/index.js.map +1 -1
- package/packages/logging/dist/server/service.d.ts +33 -0
- package/packages/logging/dist/server/service.d.ts.map +1 -1
- package/packages/logging/dist/server/service.js +63 -0
- package/packages/logging/dist/server/service.js.map +1 -1
- package/packages/logging/dist/shipper.d.ts +74 -3
- package/packages/logging/dist/shipper.d.ts.map +1 -1
- package/packages/logging/dist/shipper.js +88 -5
- package/packages/logging/dist/shipper.js.map +1 -1
- package/packages/logging/dist/singleton.d.ts.map +1 -1
- package/packages/logging/dist/singleton.js +1 -0
- package/packages/logging/dist/singleton.js.map +1 -1
- package/packages/logging/package.json +1 -1
- package/packages/logging/src/config.ts +22 -0
- package/packages/logging/src/incidents/schema.ts +0 -0
- package/packages/logging/src/index.ts +17 -0
- package/packages/logging/src/server/http.ts +42 -3
- package/packages/logging/src/server/incident-qdrant.ts +177 -0
- package/packages/logging/src/server/incident-store.ts +192 -0
- package/packages/logging/src/server/index.ts +21 -0
- package/packages/logging/src/server/service.ts +79 -0
- package/packages/logging/src/shipper.ts +120 -5
- package/packages/logging/src/singleton.ts +1 -0
- package/packages/logging/test/fanout.test.ts +186 -0
- package/packages/logging/test/incidents.test.ts +404 -0
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/dist/adapters/detail-format.d.ts +42 -0
- package/packages/notification/dist/adapters/detail-format.d.ts.map +1 -0
- package/packages/notification/dist/adapters/detail-format.js +94 -0
- package/packages/notification/dist/adapters/detail-format.js.map +1 -0
- package/packages/notification/dist/adapters/email-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/email-adapter.js +3 -2
- package/packages/notification/dist/adapters/email-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/line-adapter.d.ts +33 -0
- package/packages/notification/dist/adapters/line-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/line-adapter.js +87 -9
- package/packages/notification/dist/adapters/line-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/ntfy-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/ntfy-adapter.js +3 -2
- package/packages/notification/dist/adapters/ntfy-adapter.js.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.d.ts +14 -0
- package/packages/notification/dist/adapters/telegram-adapter.d.ts.map +1 -1
- package/packages/notification/dist/adapters/telegram-adapter.js +89 -69
- package/packages/notification/dist/adapters/telegram-adapter.js.map +1 -1
- package/packages/notification/dist/notification-center.d.ts.map +1 -1
- package/packages/notification/dist/notification-center.js +60 -7
- package/packages/notification/dist/notification-center.js.map +1 -1
- package/packages/notification/dist/pending-question.d.ts +69 -2
- package/packages/notification/dist/pending-question.d.ts.map +1 -1
- package/packages/notification/dist/pending-question.js +88 -2
- package/packages/notification/dist/pending-question.js.map +1 -1
- package/packages/notification/dist/types.d.ts +21 -0
- package/packages/notification/dist/types.d.ts.map +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/notification/src/adapters/detail-format.ts +114 -0
- package/packages/notification/src/adapters/email-adapter.ts +3 -2
- package/packages/notification/src/adapters/line-adapter.ts +97 -9
- package/packages/notification/src/adapters/ntfy-adapter.ts +3 -2
- package/packages/notification/src/adapters/telegram-adapter.ts +98 -76
- package/packages/notification/src/notification-center.ts +66 -8
- package/packages/notification/src/pending-question.ts +160 -8
- package/packages/notification/src/types.ts +21 -0
- package/packages/notification/test/approval-payload.test.ts +269 -0
- package/packages/notification/test/detail-format.test.ts +106 -0
- package/packages/notification/test/pending-question-lifecycle.test.ts +195 -0
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/dist/index.d.ts +9 -0
- package/packages/okf-indexer/dist/index.d.ts.map +1 -0
- package/packages/okf-indexer/dist/index.js +9 -0
- package/packages/okf-indexer/dist/index.js.map +1 -0
- package/packages/okf-indexer/package.json +1 -1
- package/packages/okf-indexer/src/index.ts +9 -0
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/dist/provider-router.d.ts.map +1 -1
- package/packages/provider-router/dist/types.d.ts +2 -2
- package/packages/provider-router/dist/types.d.ts.map +1 -1
- package/packages/provider-router/dist/types.js +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-router/src/types.ts +2 -2
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/dist/index.d.ts +9 -0
- package/packages/scheduler/dist/index.d.ts.map +1 -0
- package/packages/scheduler/dist/index.js +9 -0
- package/packages/scheduler/dist/index.js.map +1 -0
- package/packages/scheduler/dist/scheduler.d.ts +17 -0
- package/packages/scheduler/dist/scheduler.d.ts.map +1 -0
- package/packages/scheduler/dist/scheduler.js +41 -0
- package/packages/scheduler/dist/scheduler.js.map +1 -0
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler/src/index.ts +9 -0
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/dist/index.d.ts +7 -0
- package/packages/shared-context/dist/index.d.ts.map +1 -0
- package/packages/shared-context/dist/index.js +7 -0
- package/packages/shared-context/dist/index.js.map +1 -0
- package/packages/shared-context/dist/shared-context.d.ts +12 -0
- package/packages/shared-context/dist/shared-context.d.ts.map +1 -0
- package/packages/shared-context/dist/shared-context.js +39 -0
- package/packages/shared-context/dist/shared-context.js.map +1 -0
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/dist/client.d.ts +56 -0
- package/packages/skill-mcp-client/dist/client.d.ts.map +1 -0
- package/packages/skill-mcp-client/dist/client.js +101 -0
- package/packages/skill-mcp-client/dist/client.js.map +1 -0
- package/packages/skill-mcp-client/dist/errors.d.ts +44 -0
- package/packages/skill-mcp-client/dist/errors.d.ts.map +1 -0
- package/packages/skill-mcp-client/dist/errors.js +68 -0
- package/packages/skill-mcp-client/dist/errors.js.map +1 -0
- package/packages/skill-mcp-client/dist/index.d.ts +9 -0
- package/packages/skill-mcp-client/dist/index.d.ts.map +1 -0
- package/packages/skill-mcp-client/dist/index.js +9 -0
- package/packages/skill-mcp-client/dist/index.js.map +1 -0
- package/packages/skill-mcp-client/dist/types.d.ts +106 -0
- package/packages/skill-mcp-client/dist/types.d.ts.map +1 -0
- package/packages/skill-mcp-client/dist/types.js +14 -0
- package/packages/skill-mcp-client/dist/types.js.map +1 -0
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -5
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/telegram/src/agent-messages.d.ts +28 -0
- package/packages/telegram/src/agent-messages.js +90 -0
- package/packages/telegram/src/bindings.d.ts +296 -0
- package/packages/telegram/src/bindings.js +1086 -0
- package/packages/telegram/src/bus.d.ts +759 -0
- package/packages/telegram/src/bus.js +2732 -0
- package/packages/telegram/src/commands.d.ts +761 -0
- package/packages/telegram/src/commands.js +2622 -0
- package/packages/telegram/src/config.d.ts +222 -0
- package/packages/telegram/src/config.js +844 -0
- package/packages/telegram/src/delivery.d.ts +161 -0
- package/packages/telegram/src/delivery.js +550 -0
- package/packages/telegram/src/extension.d.ts +7 -0
- package/packages/telegram/src/extension.js +2444 -0
- package/packages/telegram/src/inbound.d.ts +94 -0
- package/packages/telegram/src/inbound.js +478 -0
- package/packages/telegram/src/index.d.ts +6 -0
- package/packages/telegram/src/index.js +6 -0
- package/packages/telegram/src/keyboard.d.ts +21 -0
- package/packages/telegram/src/keyboard.js +37 -0
- package/packages/telegram/src/lifecycle.d.ts +210 -0
- package/packages/telegram/src/lifecycle.js +538 -0
- package/packages/telegram/src/pi.d.ts +76 -0
- package/packages/telegram/src/pi.js +130 -0
- package/packages/telegram/src/polling.d.ts +332 -0
- package/packages/telegram/src/polling.js +1219 -0
- package/packages/telegram/src/replies.d.ts +210 -0
- package/packages/telegram/src/replies.js +752 -0
- package/packages/telegram/src/routing.d.ts +388 -0
- package/packages/telegram/src/routing.js +7886 -0
- package/packages/telegram/src/runtime.d.ts +159 -0
- package/packages/telegram/src/runtime.js +351 -0
- package/packages/telegram/src/telegram-api.d.ts +564 -0
- package/packages/telegram/src/telegram-api.js +1332 -0
- package/packages/telegram/src/turns.d.ts +108 -0
- package/packages/telegram/src/turns.js +482 -0
- package/packages/telegram/src/updates.d.ts +1545 -0
- package/packages/telegram/src/updates.js +6420 -0
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/dist/detector.d.ts +54 -0
- package/packages/todo-bd-sync/dist/detector.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/detector.js +132 -0
- package/packages/todo-bd-sync/dist/detector.js.map +1 -0
- package/packages/todo-bd-sync/dist/extension.d.ts +30 -0
- package/packages/todo-bd-sync/dist/extension.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/extension.js +35 -0
- package/packages/todo-bd-sync/dist/extension.js.map +1 -0
- package/packages/todo-bd-sync/dist/index.d.ts +87 -0
- package/packages/todo-bd-sync/dist/index.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/index.js +296 -0
- package/packages/todo-bd-sync/dist/index.js.map +1 -0
- package/packages/todo-bd-sync/dist/sync.d.ts +72 -0
- package/packages/todo-bd-sync/dist/sync.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/sync.js +290 -0
- package/packages/todo-bd-sync/dist/sync.js.map +1 -0
- package/packages/todo-bd-sync/dist/task-analyzer.d.ts +49 -0
- package/packages/todo-bd-sync/dist/task-analyzer.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/task-analyzer.js +240 -0
- package/packages/todo-bd-sync/dist/task-analyzer.js.map +1 -0
- package/packages/todo-bd-sync/dist/todo-reminder.d.ts +126 -0
- package/packages/todo-bd-sync/dist/todo-reminder.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/todo-reminder.js +329 -0
- package/packages/todo-bd-sync/dist/todo-reminder.js.map +1 -0
- package/packages/todo-bd-sync/dist/types.d.ts +65 -0
- package/packages/todo-bd-sync/dist/types.d.ts.map +1 -0
- package/packages/todo-bd-sync/dist/types.js +67 -0
- package/packages/todo-bd-sync/dist/types.js.map +1 -0
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/dist/index.d.ts +94 -0
- package/packages/token-estimation/dist/index.d.ts.map +1 -0
- package/packages/token-estimation/dist/index.js +115 -0
- package/packages/token-estimation/dist/index.js.map +1 -0
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/dist/ai-providers.d.ts +36 -0
- package/packages/types/dist/ai-providers.d.ts.map +1 -0
- package/packages/types/dist/ai-providers.js +66 -0
- package/packages/types/dist/ai-providers.js.map +1 -0
- package/packages/types/dist/index.d.ts +12 -0
- package/packages/types/dist/index.d.ts.map +1 -0
- package/packages/types/dist/index.js +12 -0
- package/packages/types/dist/index.js.map +1 -0
- package/packages/types/package.json +5 -5
- package/packages/types/src/index.ts +12 -0
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/worktree/worktree.ts +1 -1
- package/packages/write-review/package.json +1 -1
- package/renderer.ts +1 -1
- package/scripts/build-packages.ts +162 -0
- package/scripts/relay-client.ts +2 -2
- package/scripts/typecheck-packages.mjs +3 -3
- package/scripts/unified-webhook-server.ts +22 -10
- package/scripts/verify-package-contents.ts +345 -0
- package/scripts/ws-server.ts +1 -1
- package/tracker.ts +1 -1
- package/harness/browser-scrapers/base-browser-scraper.d.ts +0 -91
- package/harness/browser-scrapers/chrome-profile-manager.d.ts +0 -80
- package/harness/browser-scrapers/codex-browser-scraper.d.ts +0 -60
- package/harness/browser-scrapers/index.d.ts +0 -29
- package/harness/browser-scrapers/zai-browser-scraper.d.ts +0 -79
- package/harness/e2e/browser-scrapers/base-browser-scraper.d.ts +0 -92
- package/harness/e2e/browser-scrapers/chrome-profile-manager.d.ts +0 -81
- package/harness/e2e/browser-scrapers/codex-browser-scraper.d.ts +0 -61
- package/harness/e2e/browser-scrapers/index.d.ts +0 -30
- package/harness/e2e/browser-scrapers/zai-browser-scraper.d.ts +0 -80
- package/harness/langchain/agents.d.ts +0 -143
- package/harness/langchain/agents.test.d.ts +0 -2
- package/harness/langchain/checkpointer.d.ts +0 -48
- package/harness/langchain/checkpointer.test.d.ts +0 -8
- package/harness/langchain/cron-cli.d.ts +0 -12
- package/harness/langchain/daemon.d.ts +0 -161
- package/harness/langchain/daemon.test.d.ts +0 -11
- package/harness/langchain/file-tools.d.ts +0 -97
- package/harness/langchain/graph.d.ts +0 -706
- package/harness/langchain/model-router.d.ts +0 -63
- package/harness/langchain/model-router.test.d.ts +0 -2
- package/harness/langchain/models.d.ts +0 -72
- package/harness/langchain/ping-pong-middleware.d.ts +0 -28
- package/harness/langchain/ping-pong-middleware.test.d.ts +0 -2
- package/harness/langchain/run.d.ts +0 -15
- package/harness/langchain/scoreboard.test.d.ts +0 -13
- package/harness/langchain/status-line.d.ts +0 -91
- package/harness/langchain/status-line.test.d.ts +0 -8
- package/harness/langchain/surge.d.ts +0 -111
- package/harness/langchain/surge.test.d.ts +0 -10
- package/harness/langchain/widget.d.ts +0 -105
- package/harness/langchain/widget.test.d.ts +0 -8
- package/packages/autonomous-runtime/dist/worker.d.ts +0 -63
- package/packages/autonomous-runtime/dist/worker.js +0 -133
- package/packages/capability-registry/src/types.d.ts +0 -47
- package/packages/clipboard/dist/clipboard.d.ts +0 -11
- package/packages/clipboard/dist/clipboard.js +0 -40
- package/packages/clipboard/dist/index.d.ts +0 -2
- package/packages/clipboard/dist/index.js +0 -2
- package/packages/clipboard/dist/types.d.ts +0 -13
- package/packages/clipboard/dist/types.js +0 -5
- package/packages/context-discovery/dist/discover.d.ts +0 -3
- package/packages/context-discovery/dist/discover.js +0 -42
- package/packages/context-discovery/dist/discovery.d.ts +0 -28
- package/packages/context-discovery/dist/discovery.js +0 -21
- package/packages/context-discovery/dist/index.d.ts +0 -3
- package/packages/context-discovery/dist/index.js +0 -2
- package/packages/context-discovery/dist/types.d.ts +0 -16
- package/packages/context-discovery/dist/types.js +0 -2
- package/packages/event-bus/dist/bus.d.ts +0 -15
- package/packages/event-bus/dist/bus.js +0 -90
- package/packages/event-bus/dist/index.d.ts +0 -4
- package/packages/event-bus/dist/index.js +0 -3
- package/packages/event-bus/dist/test/bus.test.js +0 -52
- package/packages/event-bus/dist/types.d.ts +0 -27
- package/packages/event-bus/dist/types.js +0 -10
- package/packages/event-store/dist/index.d.ts +0 -3
- package/packages/event-store/dist/index.js +0 -2
- package/packages/event-store/dist/store.d.ts +0 -15
- package/packages/event-store/dist/store.js +0 -131
- package/packages/event-store/dist/test/store.test.js +0 -57
- package/packages/event-store/dist/types.d.ts +0 -48
- package/packages/event-store/dist/types.js +0 -5
- package/packages/event-store/src/types.d.ts +0 -48
- package/packages/file-copy-helper/dist/src/extension.js +0 -186
- package/packages/model-registry/src/types.d.ts +0 -63
- package/packages/notification/dist/src/adapters/email-adapter.d.ts +0 -17
- package/packages/notification/dist/src/adapters/email-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/adapters/email-adapter.js +0 -89
- package/packages/notification/dist/src/adapters/email-adapter.js.map +0 -1
- package/packages/notification/dist/src/adapters/line-adapter.d.ts +0 -17
- package/packages/notification/dist/src/adapters/line-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/adapters/line-adapter.js +0 -99
- package/packages/notification/dist/src/adapters/line-adapter.js.map +0 -1
- package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts +0 -17
- package/packages/notification/dist/src/adapters/ntfy-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/adapters/ntfy-adapter.js +0 -84
- package/packages/notification/dist/src/adapters/ntfy-adapter.js.map +0 -1
- package/packages/notification/dist/src/adapters/telegram-adapter.d.ts +0 -56
- package/packages/notification/dist/src/adapters/telegram-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/adapters/telegram-adapter.js +0 -251
- package/packages/notification/dist/src/adapters/telegram-adapter.js.map +0 -1
- package/packages/notification/dist/src/adapters/webhook-adapter.d.ts +0 -15
- package/packages/notification/dist/src/adapters/webhook-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/adapters/webhook-adapter.js +0 -64
- package/packages/notification/dist/src/adapters/webhook-adapter.js.map +0 -1
- package/packages/notification/dist/src/base-adapter.d.ts +0 -36
- package/packages/notification/dist/src/base-adapter.d.ts.map +0 -1
- package/packages/notification/dist/src/base-adapter.js +0 -39
- package/packages/notification/dist/src/base-adapter.js.map +0 -1
- package/packages/notification/dist/src/index.d.ts +0 -12
- package/packages/notification/dist/src/index.d.ts.map +0 -1
- package/packages/notification/dist/src/index.js +0 -12
- package/packages/notification/dist/src/index.js.map +0 -1
- package/packages/notification/dist/src/notification-center.d.ts +0 -89
- package/packages/notification/dist/src/notification-center.d.ts.map +0 -1
- package/packages/notification/dist/src/notification-center.js +0 -356
- package/packages/notification/dist/src/notification-center.js.map +0 -1
- package/packages/notification/dist/src/notification-policy.d.ts +0 -66
- package/packages/notification/dist/src/notification-policy.d.ts.map +0 -1
- package/packages/notification/dist/src/notification-policy.js +0 -153
- package/packages/notification/dist/src/notification-policy.js.map +0 -1
- package/packages/notification/dist/src/telegram-webhook-handler.d.ts +0 -86
- package/packages/notification/dist/src/telegram-webhook-handler.d.ts.map +0 -1
- package/packages/notification/dist/src/telegram-webhook-handler.js +0 -149
- package/packages/notification/dist/src/telegram-webhook-handler.js.map +0 -1
- package/packages/notification/dist/src/types.d.ts +0 -106
- package/packages/notification/dist/src/types.d.ts.map +0 -1
- package/packages/notification/dist/src/types.js +0 -7
- package/packages/notification/dist/src/types.js.map +0 -1
- package/packages/projection-engine/dist/engine.d.ts +0 -61
- package/packages/projection-engine/dist/engine.js +0 -76
- package/packages/projection-engine/dist/index.d.ts +0 -2
- package/packages/projection-engine/dist/index.js +0 -2
- package/packages/projection-engine/dist/project.d.ts +0 -3
- package/packages/projection-engine/dist/project.js +0 -24
- package/packages/projection-engine/dist/types.d.ts +0 -55
- package/packages/projection-engine/dist/types.js +0 -5
- package/packages/projection-engine/src/types.d.ts +0 -55
- package/packages/provider-adapter-sdk/dist/src/builder.d.ts +0 -140
- package/packages/provider-adapter-sdk/dist/src/builder.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/builder.js +0 -332
- package/packages/provider-adapter-sdk/dist/src/builder.js.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/errors.d.ts +0 -71
- package/packages/provider-adapter-sdk/dist/src/errors.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/errors.js +0 -111
- package/packages/provider-adapter-sdk/dist/src/errors.js.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/index.d.ts +0 -15
- package/packages/provider-adapter-sdk/dist/src/index.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/index.js +0 -19
- package/packages/provider-adapter-sdk/dist/src/index.js.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/registry.d.ts +0 -108
- package/packages/provider-adapter-sdk/dist/src/registry.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/registry.js +0 -301
- package/packages/provider-adapter-sdk/dist/src/registry.js.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/tester.d.ts +0 -71
- package/packages/provider-adapter-sdk/dist/src/tester.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/tester.js +0 -392
- package/packages/provider-adapter-sdk/dist/src/tester.js.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/types.d.ts +0 -182
- package/packages/provider-adapter-sdk/dist/src/types.d.ts.map +0 -1
- package/packages/provider-adapter-sdk/dist/src/types.js +0 -11
- package/packages/provider-adapter-sdk/dist/src/types.js.map +0 -1
- package/packages/provider-router/dist/capability-registry/src/types.d.ts +0 -47
- package/packages/provider-router/dist/capability-registry/src/types.d.ts.map +0 -1
- package/packages/provider-router/dist/capability-registry/src/types.js +0 -10
- package/packages/provider-router/dist/capability-registry/src/types.js.map +0 -1
- package/packages/provider-router/dist/provider-router/src/index.d.ts +0 -23
- package/packages/provider-router/dist/provider-router/src/index.d.ts.map +0 -1
- package/packages/provider-router/dist/provider-router/src/index.js +0 -25
- package/packages/provider-router/dist/provider-router/src/index.js.map +0 -1
- package/packages/provider-router/dist/provider-router/src/policy.d.ts +0 -10
- package/packages/provider-router/dist/provider-router/src/policy.d.ts.map +0 -1
- package/packages/provider-router/dist/provider-router/src/policy.js +0 -45
- package/packages/provider-router/dist/provider-router/src/policy.js.map +0 -1
- package/packages/provider-router/dist/provider-router/src/provider-router.d.ts +0 -56
- package/packages/provider-router/dist/provider-router/src/provider-router.d.ts.map +0 -1
- package/packages/provider-router/dist/provider-router/src/provider-router.js +0 -222
- package/packages/provider-router/dist/provider-router/src/provider-router.js.map +0 -1
- package/packages/provider-router/dist/provider-router/src/strategies.d.ts +0 -19
- package/packages/provider-router/dist/provider-router/src/strategies.d.ts.map +0 -1
- package/packages/provider-router/dist/provider-router/src/strategies.js +0 -110
- package/packages/provider-router/dist/provider-router/src/strategies.js.map +0 -1
- package/packages/provider-router/dist/provider-router/src/types.d.ts +0 -91
- package/packages/provider-router/dist/provider-router/src/types.d.ts.map +0 -1
- package/packages/provider-router/dist/provider-router/src/types.js +0 -10
- package/packages/provider-router/dist/provider-router/src/types.js.map +0 -1
- package/packages/provider-router/dist/src/index.d.ts +0 -23
- package/packages/provider-router/dist/src/index.d.ts.map +0 -1
- package/packages/provider-router/dist/src/index.js +0 -25
- package/packages/provider-router/dist/src/index.js.map +0 -1
- package/packages/provider-router/dist/src/policy.d.ts +0 -10
- package/packages/provider-router/dist/src/policy.d.ts.map +0 -1
- package/packages/provider-router/dist/src/policy.js +0 -45
- package/packages/provider-router/dist/src/policy.js.map +0 -1
- package/packages/provider-router/dist/src/provider-router.d.ts +0 -56
- package/packages/provider-router/dist/src/provider-router.d.ts.map +0 -1
- package/packages/provider-router/dist/src/provider-router.js +0 -222
- package/packages/provider-router/dist/src/provider-router.js.map +0 -1
- package/packages/provider-router/dist/src/strategies.d.ts +0 -19
- package/packages/provider-router/dist/src/strategies.d.ts.map +0 -1
- package/packages/provider-router/dist/src/strategies.js +0 -110
- package/packages/provider-router/dist/src/strategies.js.map +0 -1
- package/packages/provider-router/dist/src/types.d.ts +0 -91
- package/packages/provider-router/dist/src/types.d.ts.map +0 -1
- package/packages/provider-router/dist/src/types.js +0 -10
- package/packages/provider-router/dist/src/types.js.map +0 -1
- package/packages/provider-router/dist/types/src/runtime-types.d.ts +0 -412
- package/packages/provider-router/dist/types/src/runtime-types.d.ts.map +0 -1
- package/packages/provider-router/dist/types/src/runtime-types.js +0 -2
- package/packages/provider-router/dist/types/src/runtime-types.js.map +0 -1
- package/packages/session-api/dist/api.d.ts +0 -83
- package/packages/session-api/dist/api.js +0 -35
- package/packages/session-api/dist/index.d.ts +0 -2
- package/packages/session-api/dist/index.js +0 -2
- package/packages/session-api/dist/types.d.ts +0 -22
- package/packages/session-api/dist/types.js +0 -5
- package/packages/session-export/dist/exporter.d.ts +0 -28
- package/packages/session-export/dist/exporter.js +0 -38
- package/packages/session-export/dist/index.d.ts +0 -2
- package/packages/session-export/dist/index.js +0 -2
- package/packages/session-export/dist/types.d.ts +0 -20
- package/packages/session-export/dist/types.js +0 -5
- package/packages/subscription-engine/dist/engine.d.ts +0 -13
- package/packages/subscription-engine/dist/engine.js +0 -40
- package/packages/subscription-engine/dist/index.d.ts +0 -2
- package/packages/subscription-engine/dist/index.js +0 -2
- package/packages/subscription-engine/dist/subscriptions.d.ts +0 -19
- package/packages/subscription-engine/dist/subscriptions.js +0 -32
- package/packages/subscription-engine/dist/types.d.ts +0 -24
- package/packages/subscription-engine/dist/types.js +0 -5
- package/packages/trajectory/test/store.test.d.ts +0 -2
- package/packages/workflow-events/dist/events.d.ts +0 -38
- package/packages/workflow-events/dist/events.js +0 -46
- package/packages/workflow-events/dist/index.d.ts +0 -2
- package/packages/workflow-events/dist/index.js +0 -2
- package/packages/workflow-events/dist/pipeline.d.ts +0 -21
- package/packages/workflow-events/dist/pipeline.js +0 -27
- package/packages/workflow-events/dist/types.d.ts +0 -110
- package/packages/workflow-events/dist/types.js +0 -5
- package/packages/workflow-events/src/types.d.ts +0 -110
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { logError, logWarn } from "./file-logger.js";
|
|
13
13
|
import { existsSync, mkdirSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
|
-
import {
|
|
15
|
+
import { createRequire } from "node:module";
|
|
16
16
|
|
|
17
17
|
import type {
|
|
18
18
|
InlineKeyboardButton,
|
|
@@ -32,6 +32,23 @@ import { NtfyAdapter } from "./adapters/ntfy-adapter.js";
|
|
|
32
32
|
import { EmailAdapter } from "./adapters/email-adapter.js";
|
|
33
33
|
import { WebhookAdapter } from "./adapters/webhook-adapter.js";
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Load the SQLite binding lazily.
|
|
37
|
+
*
|
|
38
|
+
* `node:sqlite` does not exist in every runtime (Bun 1.3 has no
|
|
39
|
+
* `node:sqlite`), and a static import makes the whole module unloadable
|
|
40
|
+
* there. The persisted channel settings are an optimisation: without the
|
|
41
|
+
* database the center still works with the configured defaults.
|
|
42
|
+
*/
|
|
43
|
+
function loadDatabaseSync(): typeof import("node:sqlite").DatabaseSync | null {
|
|
44
|
+
try {
|
|
45
|
+
const require = createRequire(import.meta.url);
|
|
46
|
+
return require("node:sqlite").DatabaseSync;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
/**
|
|
36
53
|
* Pending approval request
|
|
37
54
|
*/
|
|
@@ -48,7 +65,7 @@ export class NotificationCenter {
|
|
|
48
65
|
private adapters: Map<string, ChannelAdapter> = new Map();
|
|
49
66
|
private channelEnabled: Map<string, boolean> = new Map();
|
|
50
67
|
private redactPatterns: RegExp[];
|
|
51
|
-
private db: DatabaseSync | null = null;
|
|
68
|
+
private db: import("node:sqlite").DatabaseSync | null = null;
|
|
52
69
|
/** Pending approval requests keyed by jobId */
|
|
53
70
|
private pendingApprovals: Map<string, PendingApproval> = new Map();
|
|
54
71
|
|
|
@@ -69,6 +86,15 @@ export class NotificationCenter {
|
|
|
69
86
|
*/
|
|
70
87
|
private initDatabase(): void {
|
|
71
88
|
try {
|
|
89
|
+
const DatabaseSync = loadDatabaseSync();
|
|
90
|
+
if (!DatabaseSync) {
|
|
91
|
+
logWarn(
|
|
92
|
+
"[NotificationCenter]",
|
|
93
|
+
"node:sqlite is unavailable - the channel settings are not persisted",
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
72
98
|
const dbPath = NotificationCenter.getDatabasePath();
|
|
73
99
|
this.db = new DatabaseSync(dbPath);
|
|
74
100
|
|
|
@@ -203,6 +229,14 @@ export class NotificationCenter {
|
|
|
203
229
|
const adapter = this.createAdapter(config);
|
|
204
230
|
if (adapter && adapter.isConfigured()) {
|
|
205
231
|
this.adapters.set(config.id, adapter);
|
|
232
|
+
// A registered channel without a persisted state is enabled
|
|
233
|
+
// unless its configuration says otherwise. Without this the
|
|
234
|
+
// channel is registered but silently skipped by notify(),
|
|
235
|
+
// which happens whenever the settings database is
|
|
236
|
+
// unavailable (no `node:sqlite` in the runtime).
|
|
237
|
+
if (!this.channelEnabled.has(config.id)) {
|
|
238
|
+
this.channelEnabled.set(config.id, config.enabled !== false);
|
|
239
|
+
}
|
|
206
240
|
return true;
|
|
207
241
|
}
|
|
208
242
|
} catch (error) {
|
|
@@ -515,14 +549,21 @@ export class NotificationCenter {
|
|
|
515
549
|
const approveLabel = options?.approveLabel ?? "Yes";
|
|
516
550
|
const rejectLabel = options?.rejectLabel ?? "No";
|
|
517
551
|
|
|
552
|
+
// The canonical request ID is the approval correlation key. The
|
|
553
|
+
// presentation `jobId` may carry a prefix ("waiting-user-q-123") and
|
|
554
|
+
// must not be used for the correlation: a callback that carries the
|
|
555
|
+
// prefixed value can never match the pending question, so the click
|
|
556
|
+
// is reported as stale and the approval is lost.
|
|
557
|
+
const target = context.questionId ?? context.jobId;
|
|
558
|
+
|
|
518
559
|
// Build buttons
|
|
519
560
|
const buttons: InlineKeyboardButton[] = [
|
|
520
|
-
{ text: `\u2705 ${approveLabel}`, callbackData: buildCallbackData(CallbackActions.APPROVE,
|
|
521
|
-
{ text: `\u274C ${rejectLabel}`, callbackData: buildCallbackData(CallbackActions.REJECT,
|
|
561
|
+
{ text: `\u2705 ${approveLabel}`, callbackData: buildCallbackData(CallbackActions.APPROVE, target) },
|
|
562
|
+
{ text: `\u274C ${rejectLabel}`, callbackData: buildCallbackData(CallbackActions.REJECT, target) },
|
|
522
563
|
];
|
|
523
564
|
|
|
524
565
|
// Track this pending approval so callback can resolve it
|
|
525
|
-
this.trackPendingApproval(
|
|
566
|
+
this.trackPendingApproval(target, event, context);
|
|
526
567
|
|
|
527
568
|
// Send to each channel with buttons (respecting enabled state)
|
|
528
569
|
const results: NotificationResult[] = [];
|
|
@@ -547,16 +588,23 @@ export class NotificationCenter {
|
|
|
547
588
|
results.push({ success: false, channel: id, error: String(error) });
|
|
548
589
|
}
|
|
549
590
|
} else if (adapter instanceof LineAdapter) {
|
|
550
|
-
// LINE:
|
|
591
|
+
// LINE: the same question and the same canonical request ID
|
|
592
|
+
// as Telegram, with the same Yes/No actions. The postback
|
|
593
|
+
// data uses the shared callback format, so the webhook
|
|
594
|
+
// forwards it into the same runtime resolver.
|
|
551
595
|
try {
|
|
552
596
|
const linePayload: NotificationPayload = {
|
|
553
597
|
jobId: context.jobId,
|
|
554
598
|
timestamp: new Date().toISOString(),
|
|
555
599
|
event,
|
|
556
600
|
title: "Approval Required",
|
|
557
|
-
message:
|
|
601
|
+
message: context.requirement || "A decision is needed.",
|
|
602
|
+
...(context.questionId && { questionId: context.questionId }),
|
|
558
603
|
details: {
|
|
559
604
|
jobId: context.jobId,
|
|
605
|
+
...(context.questionId && { questionId: context.questionId }),
|
|
606
|
+
...(context.questionKind && { questionKind: context.questionKind }),
|
|
607
|
+
_actionButtons: buttons,
|
|
560
608
|
...options?.additionalDetails,
|
|
561
609
|
},
|
|
562
610
|
};
|
|
@@ -712,12 +760,17 @@ export class NotificationCenter {
|
|
|
712
760
|
timestamp: new Date().toISOString(),
|
|
713
761
|
title,
|
|
714
762
|
message,
|
|
763
|
+
...(context.questionId && { questionId: context.questionId }),
|
|
715
764
|
details: {
|
|
716
765
|
jobId: context.jobId,
|
|
717
766
|
requirement: context.requirement,
|
|
718
767
|
...(context.taskId && { taskId: context.taskId }),
|
|
719
768
|
...(context.taskTitle && { taskTitle: context.taskTitle }),
|
|
720
769
|
...(context.error && { error: context.error }),
|
|
770
|
+
...(context.reason && { reason: context.reason }),
|
|
771
|
+
...(context.trigger && { trigger: context.trigger }),
|
|
772
|
+
...(context.questionId && { questionId: context.questionId }),
|
|
773
|
+
...(context.questionKind && { questionKind: context.questionKind }),
|
|
721
774
|
// Pass inline keyboard options to Telegram adapter
|
|
722
775
|
...(options?.enableInlineKeyboard && { _enableInlineKeyboard: true }),
|
|
723
776
|
...(options?.actionButtons && { _actionButtons: options.actionButtons }),
|
|
@@ -733,6 +786,11 @@ export class NotificationCenter {
|
|
|
733
786
|
const req = context.requirement ?? "";
|
|
734
787
|
const requirement =
|
|
735
788
|
req.length > 50 ? req.slice(0, 50) + "..." : req;
|
|
789
|
+
// The question the agent asks has to reach the user in full. A
|
|
790
|
+
// truncated question is not a question the user can answer, so the
|
|
791
|
+
// approval events keep the complete text and let the adapter split
|
|
792
|
+
// it for the platform.
|
|
793
|
+
const fullRequirement = req;
|
|
736
794
|
|
|
737
795
|
const map: Record<NotificationEvent, { title: string; message: string }> = {
|
|
738
796
|
JobStarted: {
|
|
@@ -781,7 +839,7 @@ export class NotificationCenter {
|
|
|
781
839
|
},
|
|
782
840
|
WaitingForUserInput: {
|
|
783
841
|
title: "Your Input Needed",
|
|
784
|
-
message: `Agent needs your input: "${
|
|
842
|
+
message: `Agent needs your input: "${fullRequirement}"${context.error ? ` (${context.error})` : ""}`,
|
|
785
843
|
},
|
|
786
844
|
AssistantResponse: {
|
|
787
845
|
title: "Assistant",
|
|
@@ -19,13 +19,31 @@ export type QuestionKind =
|
|
|
19
19
|
/**
|
|
20
20
|
* Question states
|
|
21
21
|
*/
|
|
22
|
-
export type QuestionState =
|
|
22
|
+
export type QuestionState =
|
|
23
23
|
| "pending" // Waiting for response
|
|
24
24
|
| "evaluating" // Jev is evaluating
|
|
25
25
|
| "resolved" // User answered
|
|
26
26
|
| "cancelled" // Cancelled (e.g., session switch)
|
|
27
27
|
| "expired"; // Timed out
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* The typed outcome of a Jev evaluation.
|
|
31
|
+
*
|
|
32
|
+
* It is deliberately not the same shape as a human answer: an autonomous
|
|
33
|
+
* decision is never reported as a user "yes".
|
|
34
|
+
*/
|
|
35
|
+
export type JevOutcome =
|
|
36
|
+
| "approve"
|
|
37
|
+
| "reject"
|
|
38
|
+
| "wait"
|
|
39
|
+
| "proceed_with_caution";
|
|
40
|
+
|
|
41
|
+
/** Who resolved the question. */
|
|
42
|
+
export type ResolutionSource = "user" | "jev" | "timeout";
|
|
43
|
+
|
|
44
|
+
/** The channel a human answer came from. */
|
|
45
|
+
export type ResolutionChannel = "telegram" | "line" | "pi";
|
|
46
|
+
|
|
29
47
|
/**
|
|
30
48
|
* Channel delivery states
|
|
31
49
|
*/
|
|
@@ -51,6 +69,16 @@ export interface PendingQuestion {
|
|
|
51
69
|
questionText: string;
|
|
52
70
|
/** Question kind */
|
|
53
71
|
kind: QuestionKind;
|
|
72
|
+
/** Does the proposed action need an explicit human authorization? */
|
|
73
|
+
requiresExplicitAuthorization?: boolean;
|
|
74
|
+
/** Can the proposed action destroy or damage something? */
|
|
75
|
+
destructive?: boolean;
|
|
76
|
+
/** Can the proposed action be undone? */
|
|
77
|
+
reversible?: boolean;
|
|
78
|
+
/** Does the proposed action reach the outside world? */
|
|
79
|
+
external?: boolean;
|
|
80
|
+
/** The signals the risk facts were derived from. */
|
|
81
|
+
riskSignals?: string[];
|
|
54
82
|
/** Current state */
|
|
55
83
|
state: QuestionState;
|
|
56
84
|
/** When created */
|
|
@@ -62,7 +90,13 @@ export interface PendingQuestion {
|
|
|
62
90
|
/** Resolution: "yes", "no", "clarification", or undefined */
|
|
63
91
|
resolution?: "yes" | "no" | "clarification";
|
|
64
92
|
/** Who resolved it */
|
|
65
|
-
resolvedBy?:
|
|
93
|
+
resolvedBy?: ResolutionSource;
|
|
94
|
+
/** The channel a human answer came from. Unset for a Jev resolution. */
|
|
95
|
+
resolvedChannel?: ResolutionChannel;
|
|
96
|
+
/** The Jev outcome, when the decision was autonomous. */
|
|
97
|
+
jevOutcome?: JevOutcome;
|
|
98
|
+
/** The short reason of the Jev decision. */
|
|
99
|
+
jevReason?: string;
|
|
66
100
|
/** Channel delivery states */
|
|
67
101
|
deliveryState: {
|
|
68
102
|
telegram?: ChannelDeliveryState;
|
|
@@ -413,6 +447,14 @@ export class PendingQuestionController {
|
|
|
413
447
|
workspacePath: string;
|
|
414
448
|
jobId?: string;
|
|
415
449
|
kind: QuestionKind;
|
|
450
|
+
/** The deterministic risk facts of the proposed action. */
|
|
451
|
+
risk?: {
|
|
452
|
+
requiresExplicitAuthorization?: boolean;
|
|
453
|
+
destructive?: boolean;
|
|
454
|
+
reversible?: boolean;
|
|
455
|
+
external?: boolean;
|
|
456
|
+
signals?: string[];
|
|
457
|
+
};
|
|
416
458
|
}): PendingQuestion {
|
|
417
459
|
const requestId = this.generateRequestId();
|
|
418
460
|
|
|
@@ -423,6 +465,11 @@ export class PendingQuestionController {
|
|
|
423
465
|
jobId: params.jobId,
|
|
424
466
|
questionText: params.text,
|
|
425
467
|
kind: params.kind,
|
|
468
|
+
requiresExplicitAuthorization: params.risk?.requiresExplicitAuthorization,
|
|
469
|
+
destructive: params.risk?.destructive,
|
|
470
|
+
reversible: params.risk?.reversible,
|
|
471
|
+
external: params.risk?.external,
|
|
472
|
+
riskSignals: params.risk?.signals,
|
|
426
473
|
state: "pending",
|
|
427
474
|
createdAt: new Date(),
|
|
428
475
|
dueAt: new Date(Date.now() + this.config.evaluationDelayMs),
|
|
@@ -482,9 +529,11 @@ export class PendingQuestionController {
|
|
|
482
529
|
*/
|
|
483
530
|
resolve(params: {
|
|
484
531
|
resolution: "yes" | "no" | "clarification";
|
|
485
|
-
by:
|
|
532
|
+
by: ResolutionSource;
|
|
486
533
|
/** Optional requestId to validate (first-answer-wins) */
|
|
487
534
|
requestId?: string;
|
|
535
|
+
/** The channel a human answer came from. */
|
|
536
|
+
channel?: ResolutionChannel;
|
|
488
537
|
}): boolean {
|
|
489
538
|
if (!this.currentQuestion) {
|
|
490
539
|
logWarn("[PendingQuestion]", "No question to resolve - no pending question");
|
|
@@ -504,22 +553,125 @@ export class PendingQuestionController {
|
|
|
504
553
|
`Question already resolved as ${this.currentQuestion.resolution} - duplicate callback ignored`);
|
|
505
554
|
return false;
|
|
506
555
|
}
|
|
507
|
-
|
|
556
|
+
|
|
508
557
|
this.currentQuestion.state = "resolved";
|
|
509
558
|
this.currentQuestion.resolution = params.resolution;
|
|
510
559
|
this.currentQuestion.resolvedBy = params.by;
|
|
511
560
|
this.currentQuestion.resolvedAt = new Date();
|
|
512
|
-
|
|
561
|
+
if (params.by === "user" && params.channel) {
|
|
562
|
+
this.currentQuestion.resolvedChannel = params.channel;
|
|
563
|
+
}
|
|
564
|
+
|
|
513
565
|
logWarn("[PendingQuestion]", `Resolved question ${this.currentQuestion.requestId}: ${params.resolution} by ${params.by}`);
|
|
514
|
-
|
|
515
|
-
// Notify callback
|
|
516
|
-
|
|
566
|
+
|
|
567
|
+
// Notify callback. A Jev resolution is NOT a user answer: it must never
|
|
568
|
+
// be dispatched as a synthetic "yes" into the conversation.
|
|
569
|
+
if (params.by === "jev") {
|
|
570
|
+
if (this.jevDecisionCallback) {
|
|
571
|
+
this.jevDecisionCallback(this.currentQuestion, {
|
|
572
|
+
action: "continue",
|
|
573
|
+
reason: "resolved by jev",
|
|
574
|
+
eligible: true,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
} else if (this.userAnswerCallback) {
|
|
517
578
|
this.userAnswerCallback(this.currentQuestion, params.resolution);
|
|
518
579
|
}
|
|
519
580
|
|
|
520
581
|
return true;
|
|
521
582
|
}
|
|
522
583
|
|
|
584
|
+
/**
|
|
585
|
+
* Resolve a question with an autonomous Jev decision.
|
|
586
|
+
*
|
|
587
|
+
* The question is closed with `resolvedBy: "jev"`, the outcome is recorded
|
|
588
|
+
* and the Jev callback is notified. No user answer is dispatched: nobody
|
|
589
|
+
* clicked anything.
|
|
590
|
+
*
|
|
591
|
+
* @returns true if the question was closed, false when it was stale or
|
|
592
|
+
* already answered by a human.
|
|
593
|
+
*/
|
|
594
|
+
resolveByJev(
|
|
595
|
+
requestId: string,
|
|
596
|
+
outcome: JevOutcome,
|
|
597
|
+
reason?: string,
|
|
598
|
+
): boolean {
|
|
599
|
+
if (!this.currentQuestion) {
|
|
600
|
+
logWarn("[PendingQuestion]", "No question to resolve - no pending question");
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (this.currentQuestion.requestId !== requestId) {
|
|
605
|
+
logWarn("[PendingQuestion]",
|
|
606
|
+
`Jev resolution for an unknown request: ${requestId} (current=${this.currentQuestion.requestId})`);
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (this.currentQuestion.state === "resolved") {
|
|
611
|
+
logWarn("[PendingQuestion]",
|
|
612
|
+
`Question already resolved as ${this.currentQuestion.resolution} - jev decision ignored`);
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
this.currentQuestion.state = "resolved";
|
|
617
|
+
this.currentQuestion.resolvedBy = "jev";
|
|
618
|
+
this.currentQuestion.jevOutcome = outcome;
|
|
619
|
+
this.currentQuestion.jevReason = reason;
|
|
620
|
+
this.currentQuestion.resolvedAt = new Date();
|
|
621
|
+
|
|
622
|
+
logWarn("[PendingQuestion]",
|
|
623
|
+
`Closed question ${this.currentQuestion.requestId} with jev outcome ${outcome}`);
|
|
624
|
+
|
|
625
|
+
if (this.jevDecisionCallback) {
|
|
626
|
+
this.jevDecisionCallback(this.currentQuestion, {
|
|
627
|
+
action: outcome === "reject" ? "stop" : outcome === "wait" ? "wait" : "continue",
|
|
628
|
+
reason: reason ?? `jev:${outcome}`,
|
|
629
|
+
eligible: true,
|
|
630
|
+
metadata: { outcome },
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Apply an approval action from a channel to the current question.
|
|
639
|
+
*
|
|
640
|
+
* The action names are the shared callback actions, so Telegram and LINE
|
|
641
|
+
* use the same values and the same resolver. A stale request ID, a
|
|
642
|
+
* duplicate click or a wrong session leaves the running wait untouched.
|
|
643
|
+
*/
|
|
644
|
+
applyApproval(
|
|
645
|
+
action: string,
|
|
646
|
+
requestId: string,
|
|
647
|
+
channel: ResolutionChannel,
|
|
648
|
+
): { applied: boolean; resolution?: "yes" | "no"; reason?: string } {
|
|
649
|
+
if (action !== "approve" && action !== "reject") {
|
|
650
|
+
return { applied: false, reason: `unknown action '${action}'` };
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const resolution = action === "approve" ? "yes" : "no";
|
|
654
|
+
const applied = this.resolve({ resolution, by: "user", requestId, channel });
|
|
655
|
+
|
|
656
|
+
return applied
|
|
657
|
+
? { applied: true, resolution }
|
|
658
|
+
: { applied: false, resolution, reason: "stale or duplicate request ID" };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Is the request ID still actionable?
|
|
663
|
+
*
|
|
664
|
+
* A button of a closed question is stale: it must not resolve another
|
|
665
|
+
* question and it must not clear a running wait.
|
|
666
|
+
*/
|
|
667
|
+
isActionable(requestId: string): boolean {
|
|
668
|
+
if (!this.currentQuestion) return false;
|
|
669
|
+
return (
|
|
670
|
+
this.currentQuestion.requestId === requestId &&
|
|
671
|
+
(this.currentQuestion.state === "pending" || this.currentQuestion.state === "evaluating")
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
|
|
523
675
|
/**
|
|
524
676
|
* Cancel the current question (e.g., on session switch)
|
|
525
677
|
*/
|
|
@@ -37,6 +37,8 @@ export interface NotificationPayload {
|
|
|
37
37
|
// Inline keyboard buttons for Telegram
|
|
38
38
|
enableInlineKeyboard?: boolean;
|
|
39
39
|
actionButtons?: InlineKeyboardButton[];
|
|
40
|
+
/** The canonical pending-question request ID (the approval correlation key). */
|
|
41
|
+
questionId?: string;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export interface NotificationConfig {
|
|
@@ -148,5 +150,24 @@ export interface NotificationContext {
|
|
|
148
150
|
requirement: string;
|
|
149
151
|
taskId?: string;
|
|
150
152
|
taskTitle?: string;
|
|
153
|
+
/** A real failure. Only error events render this under an "Error:" label. */
|
|
151
154
|
error?: string;
|
|
155
|
+
/** Why the event happened (a resume trigger, a quota detail, a review reason). */
|
|
156
|
+
reason?: string;
|
|
157
|
+
/**
|
|
158
|
+
* Why the work resumed ("post-compact", "provider-overload"). A trigger is
|
|
159
|
+
* not a failure: it renders as "Trigger: ..." in a human wording, never
|
|
160
|
+
* under an "Error:" label.
|
|
161
|
+
*/
|
|
162
|
+
trigger?: string;
|
|
163
|
+
/**
|
|
164
|
+
* The canonical pending-question request ID.
|
|
165
|
+
*
|
|
166
|
+
* It is the approval correlation key: the callback data of every Yes/No
|
|
167
|
+
* action points at it. The presentation `jobId` may carry a prefix and
|
|
168
|
+
* must not be used for the correlation.
|
|
169
|
+
*/
|
|
170
|
+
questionId?: string;
|
|
171
|
+
/** The kind of the pending question (approval, clarification, ...). */
|
|
172
|
+
questionKind?: string;
|
|
152
173
|
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Approval payload tests.
|
|
3
|
+
*
|
|
4
|
+
* The question has to reach the user complete, and the Yes/No actions have
|
|
5
|
+
* to point at the canonical request ID - on Telegram and on LINE. A button
|
|
6
|
+
* that carries a prefixed job ID can never match the pending question, so
|
|
7
|
+
* every click is reported as stale and the approval is lost.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
11
|
+
import { NotificationCenter } from "../src/notification-center.js";
|
|
12
|
+
import { PendingQuestionController } from "../src/pending-question.js";
|
|
13
|
+
import { parseCallbackData } from "../src/telegram-webhook-handler.js";
|
|
14
|
+
import type { NotificationConfig } from "../src/types.js";
|
|
15
|
+
|
|
16
|
+
const LONG_QUESTION =
|
|
17
|
+
"Want me to implement the persistence layer with the SQLite schema, the migration runner and the rollback path, and then wire it into the harness? ";
|
|
18
|
+
|
|
19
|
+
interface CapturedRequest {
|
|
20
|
+
url: string;
|
|
21
|
+
body: Record<string, any>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let captured: CapturedRequest[] = [];
|
|
25
|
+
let originalFetch: typeof globalThis.fetch;
|
|
26
|
+
|
|
27
|
+
function createCenter(): NotificationCenter {
|
|
28
|
+
const config: NotificationConfig = {
|
|
29
|
+
channels: [
|
|
30
|
+
{
|
|
31
|
+
id: "telegram",
|
|
32
|
+
type: "telegram",
|
|
33
|
+
enabled: true,
|
|
34
|
+
config: {
|
|
35
|
+
botToken: "test-bot-token",
|
|
36
|
+
chatId: "12345",
|
|
37
|
+
enableInlineKeyboard: true,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "line",
|
|
42
|
+
type: "line",
|
|
43
|
+
enabled: true,
|
|
44
|
+
config: { channelAccessToken: "test-line-token", userId: "U-test" },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
return new NotificationCenter(config);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function telegramRequests(): CapturedRequest[] {
|
|
52
|
+
return captured.filter((r) => r.url.includes("api.telegram.org"));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function lineRequests(): CapturedRequest[] {
|
|
56
|
+
return captured.filter((r) => r.url.includes("api.line.me"));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
captured = [];
|
|
61
|
+
originalFetch = globalThis.fetch;
|
|
62
|
+
globalThis.fetch = (async (input: any, init?: any) => {
|
|
63
|
+
const url = typeof input === "string" ? input : (input?.url ?? "");
|
|
64
|
+
let body: any = {};
|
|
65
|
+
try {
|
|
66
|
+
body = JSON.parse(init?.body ?? "{}");
|
|
67
|
+
} catch {
|
|
68
|
+
body = {};
|
|
69
|
+
}
|
|
70
|
+
captured.push({ url, body });
|
|
71
|
+
|
|
72
|
+
// The content filter asks Jev. Without a configured key the answer
|
|
73
|
+
// does not matter, and a failure keeps the test offline.
|
|
74
|
+
if (url.includes("typesafe.ai")) {
|
|
75
|
+
return new Response("unauthorized", { status: 401 });
|
|
76
|
+
}
|
|
77
|
+
return new Response(JSON.stringify({ ok: true, result: {} }), {
|
|
78
|
+
status: 200,
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
});
|
|
81
|
+
}) as typeof globalThis.fetch;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(() => {
|
|
85
|
+
globalThis.fetch = originalFetch;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("approval payload", () => {
|
|
89
|
+
it("sends the complete question to Telegram without truncating it", async () => {
|
|
90
|
+
const center = createCenter();
|
|
91
|
+
const question = LONG_QUESTION.repeat(3);
|
|
92
|
+
|
|
93
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
94
|
+
jobId: "q-long",
|
|
95
|
+
questionId: "q-long",
|
|
96
|
+
requirement: question,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const text = telegramRequests()
|
|
100
|
+
.map((r) => String(r.body.text ?? ""))
|
|
101
|
+
.join("\n");
|
|
102
|
+
|
|
103
|
+
expect(text).toContain(question.trim());
|
|
104
|
+
// The shared 50 character summary must not be used for a question.
|
|
105
|
+
expect(text).not.toContain("...");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("sends the complete question to LINE", async () => {
|
|
109
|
+
const center = createCenter();
|
|
110
|
+
const question = LONG_QUESTION.repeat(3);
|
|
111
|
+
|
|
112
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
113
|
+
jobId: "q-long",
|
|
114
|
+
questionId: "q-long",
|
|
115
|
+
requirement: question,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const line = lineRequests()[0];
|
|
119
|
+
const messages = line?.body?.messages ?? [];
|
|
120
|
+
const textMessage = messages.find((m: any) => m.type === "text");
|
|
121
|
+
|
|
122
|
+
expect(textMessage).toBeDefined();
|
|
123
|
+
expect(String(textMessage.text)).toContain(question.trim());
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("puts the canonical request ID into the Telegram callback data", async () => {
|
|
127
|
+
const center = createCenter();
|
|
128
|
+
|
|
129
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
130
|
+
jobId: "waiting-user-q-123",
|
|
131
|
+
questionId: "q-123",
|
|
132
|
+
requirement: "Want me to continue?",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const telegram = telegramRequests()[0];
|
|
136
|
+
const keyboard = telegram?.body?.reply_markup;
|
|
137
|
+
expect(keyboard).toBeDefined();
|
|
138
|
+
|
|
139
|
+
const approve = keyboard.inline_keyboard[0][0];
|
|
140
|
+
const reject = keyboard.inline_keyboard[0][1];
|
|
141
|
+
|
|
142
|
+
const parsedApprove = parseCallbackData(approve.callback_data);
|
|
143
|
+
const parsedReject = parseCallbackData(reject.callback_data);
|
|
144
|
+
|
|
145
|
+
expect(parsedApprove.action).toBe("approve");
|
|
146
|
+
expect(parsedReject.action).toBe("reject");
|
|
147
|
+
// The canonical ID, not the prefixed presentation job ID.
|
|
148
|
+
expect(parsedApprove.targetId).toBe("q-123");
|
|
149
|
+
expect(parsedReject.targetId).toBe("q-123");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("resolves the matching pending question from the real callback data", async () => {
|
|
153
|
+
const center = createCenter();
|
|
154
|
+
const controller = new PendingQuestionController();
|
|
155
|
+
const question = controller.createQuestion({
|
|
156
|
+
text: "Want me to continue?",
|
|
157
|
+
sessionId: "session-1",
|
|
158
|
+
workspacePath: "/tmp",
|
|
159
|
+
kind: "approval",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
163
|
+
jobId: `waiting-user-${question.requestId}`,
|
|
164
|
+
questionId: question.requestId,
|
|
165
|
+
requirement: "Want me to continue?",
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const telegram = telegramRequests()[0];
|
|
169
|
+
const callbackData = telegram.body.reply_markup.inline_keyboard[0][0]
|
|
170
|
+
.callback_data as string;
|
|
171
|
+
const parsed = parseCallbackData(callbackData);
|
|
172
|
+
|
|
173
|
+
const applied = controller.applyApproval(
|
|
174
|
+
parsed.action,
|
|
175
|
+
parsed.targetId,
|
|
176
|
+
"telegram",
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(applied.applied).toBe(true);
|
|
180
|
+
expect(applied.resolution).toBe("yes");
|
|
181
|
+
expect(controller.getResolution()).toBe("yes");
|
|
182
|
+
expect(controller.getCurrentQuestion()?.resolvedBy).toBe("user");
|
|
183
|
+
expect(controller.getCurrentQuestion()?.resolvedChannel).toBe("telegram");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("uses the same canonical ID for the LINE postback actions", async () => {
|
|
187
|
+
const center = createCenter();
|
|
188
|
+
|
|
189
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
190
|
+
jobId: "waiting-user-q-123",
|
|
191
|
+
questionId: "q-123",
|
|
192
|
+
requirement: "Want me to continue?",
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const line = lineRequests()[0];
|
|
196
|
+
const messages = line?.body?.messages ?? [];
|
|
197
|
+
const template = messages.find((m: any) => m.type === "template");
|
|
198
|
+
expect(template).toBeDefined();
|
|
199
|
+
|
|
200
|
+
const actions = template.template.actions;
|
|
201
|
+
expect(actions).toHaveLength(2);
|
|
202
|
+
expect(actions[0].type).toBe("postback");
|
|
203
|
+
expect(actions[1].type).toBe("postback");
|
|
204
|
+
|
|
205
|
+
const approve = parseCallbackData(actions[0].data);
|
|
206
|
+
const reject = parseCallbackData(actions[1].data);
|
|
207
|
+
|
|
208
|
+
expect(approve.action).toBe("approve");
|
|
209
|
+
expect(reject.action).toBe("reject");
|
|
210
|
+
expect(approve.targetId).toBe("q-123");
|
|
211
|
+
expect(reject.targetId).toBe("q-123");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("routes the LINE postback into the same resolver as Telegram", async () => {
|
|
215
|
+
const center = createCenter();
|
|
216
|
+
const controller = new PendingQuestionController();
|
|
217
|
+
const question = controller.createQuestion({
|
|
218
|
+
text: "Want me to continue?",
|
|
219
|
+
sessionId: "session-1",
|
|
220
|
+
workspacePath: "/tmp",
|
|
221
|
+
kind: "approval",
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
225
|
+
jobId: `waiting-user-${question.requestId}`,
|
|
226
|
+
questionId: question.requestId,
|
|
227
|
+
requirement: "Want me to continue?",
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const line = lineRequests()[0];
|
|
231
|
+
const messages = line?.body?.messages ?? [];
|
|
232
|
+
const template = messages.find((m: any) => m.type === "template");
|
|
233
|
+
const parsed = parseCallbackData(template.template.actions[0].data);
|
|
234
|
+
|
|
235
|
+
const applied = controller.applyApproval(
|
|
236
|
+
parsed.action,
|
|
237
|
+
parsed.targetId,
|
|
238
|
+
"line",
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(applied.applied).toBe(true);
|
|
242
|
+
expect(controller.getResolution()).toBe("yes");
|
|
243
|
+
expect(controller.getCurrentQuestion()?.resolvedChannel).toBe("line");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("attaches the keyboard only to the final chunk of a long question", async () => {
|
|
247
|
+
const center = createCenter();
|
|
248
|
+
const question = `${LONG_QUESTION.repeat(30)}Want me to continue?`;
|
|
249
|
+
|
|
250
|
+
await center.notifyWithApproval("WaitingForUserInput", {
|
|
251
|
+
jobId: "q-chunked",
|
|
252
|
+
questionId: "q-chunked",
|
|
253
|
+
requirement: question,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const requests = telegramRequests();
|
|
257
|
+
expect(requests.length).toBeGreaterThan(1);
|
|
258
|
+
|
|
259
|
+
const withKeyboard = requests.filter((r) => r.body?.reply_markup);
|
|
260
|
+
expect(withKeyboard).toHaveLength(1);
|
|
261
|
+
// The actionable message is the last one.
|
|
262
|
+
expect(withKeyboard[0]).toBe(requests[requests.length - 1]);
|
|
263
|
+
|
|
264
|
+
// The question stays understandable across the chunks.
|
|
265
|
+
const allText = requests.map((r) => String(r.body.text ?? "")).join("");
|
|
266
|
+
expect(allText).toContain("Want me to continue?");
|
|
267
|
+
expect(allText).toContain("[Part 1/");
|
|
268
|
+
});
|
|
269
|
+
});
|