fix(tools): clear timeout timers and update audit state
This commit is contained in:
@@ -3,6 +3,13 @@
|
|||||||
Date: 2026-02-16
|
Date: 2026-02-16
|
||||||
Scope: Production-risk-first audit of bugs, code improvements, and feature opportunities.
|
Scope: Production-risk-first audit of bugs, code improvements, and feature opportunities.
|
||||||
|
|
||||||
|
## Remediation Status (2026-02-16)
|
||||||
|
|
||||||
|
- ✅ F-001 addressed: chat markdown rendering now sanitizes HTML before DOM insertion in `src/gateway/ui/pages/chat.js` (and legacy `src/gateway/ui/chat.html`).
|
||||||
|
- ✅ F-006 addressed: inbound HTTP request bodies now enforce a configurable max-size limit (`server.max_request_body_bytes`) with `413 Payload Too Large` responses.
|
||||||
|
- ✅ F-007 addressed: `ToolExecutor` timeout timer handles are now cleared in `finally`, preventing orphan timers on fast/failed tool calls.
|
||||||
|
- ✅ F-016 partially addressed: gateway + webhook body readers were consolidated into shared utility `src/utils/httpBody.ts` with size-limit enforcement.
|
||||||
|
|
||||||
## Executive Summary
|
## Executive Summary
|
||||||
|
|
||||||
Current health snapshot:
|
Current health snapshot:
|
||||||
|
|||||||
+131
-46
@@ -2,7 +2,6 @@
|
|||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"updated_at": "2026-02-16",
|
"updated_at": "2026-02-16",
|
||||||
"description": "Tracks the status of all Flynn plans and implementation phases",
|
"description": "Tracks the status of all Flynn plans and implementation phases",
|
||||||
|
|
||||||
"plans": {
|
"plans": {
|
||||||
"docs-agent-oriented-diagrams-pass": {
|
"docs-agent-oriented-diagrams-pass": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run + pnpm typecheck passing"
|
"test_status": "pnpm test:run + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"zai-auth-resolution-and-401-hints": {
|
"zai-auth-resolution-and-401-hints": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -56,7 +54,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/daemon/clientFactory.test.ts src/models/openai.test.ts + pnpm typecheck passing (updated to cover textual 401 without status field)"
|
"test_status": "pnpm test:run src/daemon/clientFactory.test.ts src/models/openai.test.ts + pnpm typecheck passing (updated to cover textual 401 without status field)"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tui-model-provider-switch-activates-tier": {
|
"tui-model-provider-switch-activates-tier": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -68,7 +65,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/frontends/tui/minimal.test.ts src/models/openai.test.ts src/daemon/clientFactory.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/frontends/tui/minimal.test.ts src/models/openai.test.ts src/daemon/clientFactory.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"zai-auth-reauthenticate-confirmation": {
|
"zai-auth-reauthenticate-confirmation": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -80,7 +76,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/cli/zai-auth.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/cli/zai-auth.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"auth-commands-reauthenticate-confirmation": {
|
"auth-commands-reauthenticate-confirmation": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -96,7 +91,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/cli/openai-key.test.ts src/cli/anthropic-auth.test.ts src/cli/openai-auth.test.ts src/cli/zai-auth.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/cli/openai-key.test.ts src/cli/anthropic-auth.test.ts src/cli/openai-auth.test.ts src/cli/zai-auth.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"cli-suppress-punycode-deprecation-warning": {
|
"cli-suppress-punycode-deprecation-warning": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -109,7 +103,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/cli/suppressNodeWarnings.test.ts src/cli/index.test.ts + pnpm typecheck + pnpm build passing"
|
"test_status": "pnpm test:run src/cli/suppressNodeWarnings.test.ts src/cli/index.test.ts + pnpm typecheck + pnpm build passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"zai-auth-mode-selection-api-vs-plan": {
|
"zai-auth-mode-selection-api-vs-plan": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -122,7 +115,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/cli/zai-auth.test.ts src/frontends/tui/minimal.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/cli/zai-auth.test.ts src/frontends/tui/minimal.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"tui-login-reauth-confirmation-all-providers": {
|
"tui-login-reauth-confirmation-all-providers": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -134,7 +126,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/frontends/tui/minimal.login.test.ts src/frontends/tui/minimal.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/frontends/tui/minimal.login.test.ts src/frontends/tui/minimal.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"anthropic-auth-mode-flag": {
|
"anthropic-auth-mode-flag": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -146,7 +137,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/cli/anthropic-auth.test.ts src/cli/index.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/cli/anthropic-auth.test.ts src/cli/index.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"deployment-port-env-override": {
|
"deployment-port-env-override": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -158,7 +148,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run + pnpm typecheck passing"
|
"test_status": "pnpm test:run + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"deployment-targets-nix": {
|
"deployment-targets-nix": {
|
||||||
"file": "2026-02-16-deployment-targets.md",
|
"file": "2026-02-16-deployment-targets.md",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -178,7 +167,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "nix build dependency hash resolved (fetchPnpmDeps hash pinned from build output); run nix build .#flynn to verify end-to-end in host environment"
|
"test_status": "nix build dependency hash resolved (fetchPnpmDeps hash pinned from build output); run nix build .#flynn to verify end-to-end in host environment"
|
||||||
},
|
},
|
||||||
|
|
||||||
"deployment-targets-paas": {
|
"deployment-targets-paas": {
|
||||||
"file": "2026-02-16-deployment-targets.md",
|
"file": "2026-02-16-deployment-targets.md",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -200,7 +188,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm typecheck passing (no runtime code changes)"
|
"test_status": "pnpm typecheck passing (no runtime code changes)"
|
||||||
},
|
},
|
||||||
|
|
||||||
"openclaw-gap-roadmap": {
|
"openclaw-gap-roadmap": {
|
||||||
"file": "2026-02-15-openclaw-gap-roadmap.md",
|
"file": "2026-02-15-openclaw-gap-roadmap.md",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -372,7 +359,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm typecheck + pnpm test:run passing"
|
"test_status": "pnpm typecheck + pnpm test:run passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"elevated-mode": {
|
"elevated-mode": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-16",
|
"date": "2026-02-16",
|
||||||
@@ -394,7 +380,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm typecheck + pnpm test:run passing"
|
"test_status": "pnpm typecheck + pnpm test:run passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"matrix-channel-adapter": {
|
"matrix-channel-adapter": {
|
||||||
"file": "2026-02-15-matrix-channel-adapter.md",
|
"file": "2026-02-15-matrix-channel-adapter.md",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -611,7 +596,10 @@
|
|||||||
"priority": "P0",
|
"priority": "P0",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"description": "Persistent memory with file-based storage, memory tools, auto-extraction after compaction",
|
"description": "Persistent memory with file-based storage, memory tools, auto-extraction after compaction",
|
||||||
"depends_on": ["phase_0", "phase_1"],
|
"depends_on": [
|
||||||
|
"phase_0",
|
||||||
|
"phase_1"
|
||||||
|
],
|
||||||
"files_created": [
|
"files_created": [
|
||||||
"src/memory/store.ts",
|
"src/memory/store.ts",
|
||||||
"src/memory/store.test.ts",
|
"src/memory/store.test.ts",
|
||||||
@@ -664,7 +652,9 @@
|
|||||||
"src/daemon/index.ts",
|
"src/daemon/index.ts",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"new_dependencies": ["@slack/bolt"],
|
"new_dependencies": [
|
||||||
|
"@slack/bolt"
|
||||||
|
],
|
||||||
"test_status": "22/22 passing",
|
"test_status": "22/22 passing",
|
||||||
"notes": "Socket Mode only (HTTP fallback deferred). Slash commands deferred. User ID used as senderName (display name resolution is a follow-up)."
|
"notes": "Socket Mode only (HTTP fallback deferred). Slash commands deferred. User ID used as senderName (display name resolution is a follow-up)."
|
||||||
},
|
},
|
||||||
@@ -682,7 +672,9 @@
|
|||||||
"src/daemon/index.ts",
|
"src/daemon/index.ts",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"new_dependencies": ["whatsapp-web.js"],
|
"new_dependencies": [
|
||||||
|
"whatsapp-web.js"
|
||||||
|
],
|
||||||
"test_status": "25/25 passing",
|
"test_status": "25/25 passing",
|
||||||
"notes": "QR code auth via LocalAuth. Session persistence via data_dir. Group messages filtered (DM only). Phone number allowlist. Headless Chrome with Puppeteer."
|
"notes": "QR code auth via LocalAuth. Session persistence via data_dir. Group messages filtered (DM only). Phone number allowlist. Headless Chrome with Puppeteer."
|
||||||
}
|
}
|
||||||
@@ -748,7 +740,12 @@
|
|||||||
"src/tools/builtin/web-fetch.ts",
|
"src/tools/builtin/web-fetch.ts",
|
||||||
"src/tools/builtin/web-fetch.test.ts"
|
"src/tools/builtin/web-fetch.test.ts"
|
||||||
],
|
],
|
||||||
"new_dependencies": ["turndown", "linkedom", "@mozilla/readability", "@types/turndown"],
|
"new_dependencies": [
|
||||||
|
"turndown",
|
||||||
|
"linkedom",
|
||||||
|
"@mozilla/readability",
|
||||||
|
"@types/turndown"
|
||||||
|
],
|
||||||
"test_status": "10/10 passing"
|
"test_status": "10/10 passing"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -953,22 +950,30 @@
|
|||||||
"telegram": {
|
"telegram": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"description": "Handle message:photo (largest size, download via getFile API, base64) and image message:document events with caption text",
|
"description": "Handle message:photo (largest size, download via getFile API, base64) and image message:document events with caption text",
|
||||||
"files_modified": ["src/channels/telegram/adapter.ts"]
|
"files_modified": [
|
||||||
|
"src/channels/telegram/adapter.ts"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"discord": {
|
"discord": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"description": "Extract image attachments from message.attachments Collection, pass Discord CDN URLs directly",
|
"description": "Extract image attachments from message.attachments Collection, pass Discord CDN URLs directly",
|
||||||
"files_modified": ["src/channels/discord/adapter.ts"]
|
"files_modified": [
|
||||||
|
"src/channels/discord/adapter.ts"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"slack": {
|
"slack": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"description": "Download image files via url_private_download with bot token auth, base64 encode",
|
"description": "Download image files via url_private_download with bot token auth, base64 encode",
|
||||||
"files_modified": ["src/channels/slack/adapter.ts"]
|
"files_modified": [
|
||||||
|
"src/channels/slack/adapter.ts"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"whatsapp": {
|
"whatsapp": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"description": "Use downloadMedia() from whatsapp-web.js (returns base64 natively)",
|
"description": "Use downloadMedia() from whatsapp-web.js (returns base64 natively)",
|
||||||
"files_modified": ["src/channels/whatsapp/adapter.ts"]
|
"files_modified": [
|
||||||
|
"src/channels/whatsapp/adapter.ts"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"webchat": {
|
"webchat": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@@ -1250,12 +1255,13 @@
|
|||||||
"src/daemon/index.ts",
|
"src/daemon/index.ts",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"new_dependencies": ["googleapis"],
|
"new_dependencies": [
|
||||||
|
"googleapis"
|
||||||
|
],
|
||||||
"test_status": "16/16 passing"
|
"test_status": "16/16 passing"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"gmail-push-revisit": {
|
"gmail-push-revisit": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-13",
|
"date": "2026-02-13",
|
||||||
@@ -1278,10 +1284,11 @@
|
|||||||
"package.json",
|
"package.json",
|
||||||
"pnpm-lock.yaml"
|
"pnpm-lock.yaml"
|
||||||
],
|
],
|
||||||
"new_dependencies": ["@google-cloud/pubsub"],
|
"new_dependencies": [
|
||||||
|
"@google-cloud/pubsub"
|
||||||
|
],
|
||||||
"test_status": "pnpm typecheck + pnpm test:run passing"
|
"test_status": "pnpm typecheck + pnpm test:run passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"openai-oauth-codex": {
|
"openai-oauth-codex": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-13",
|
"date": "2026-02-13",
|
||||||
@@ -1304,7 +1311,6 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm typecheck + pnpm test:run passing"
|
"test_status": "pnpm typecheck + pnpm test:run passing"
|
||||||
},
|
},
|
||||||
|
|
||||||
"zai-glm-4.7-credential-integration": {
|
"zai-glm-4.7-credential-integration": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-13",
|
"date": "2026-02-13",
|
||||||
@@ -1566,7 +1572,9 @@
|
|||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-09",
|
"date": "2026-02-09",
|
||||||
"summary": "Configurable log-level system to suppress noisy fallback debug output in TUI. Replaces console.debug/log/warn with structured logger respecting config log_level (default: info).",
|
"summary": "Configurable log-level system to suppress noisy fallback debug output in TUI. Replaces console.debug/log/warn with structured logger respecting config log_level (default: info).",
|
||||||
"files_created": ["src/logger.ts"],
|
"files_created": [
|
||||||
|
"src/logger.ts"
|
||||||
|
],
|
||||||
"files_modified": [
|
"files_modified": [
|
||||||
"src/models/router.ts",
|
"src/models/router.ts",
|
||||||
"src/models/retry.ts",
|
"src/models/retry.ts",
|
||||||
@@ -2360,26 +2368,103 @@
|
|||||||
},
|
},
|
||||||
"earlier_plans": {
|
"earlier_plans": {
|
||||||
"plans": [
|
"plans": [
|
||||||
{ "file": "2026-02-02-flynn-design.md", "status": "completed" },
|
{
|
||||||
{ "file": "2026-02-02-flynn-phase1-implementation.md", "status": "completed" },
|
"file": "2026-02-02-flynn-design.md",
|
||||||
{ "file": "2026-02-02-flynn-phase2-implementation.md", "status": "completed" },
|
"status": "completed"
|
||||||
{ "file": "2026-02-05-flynn-phase3-implementation.md", "status": "completed" },
|
},
|
||||||
{ "file": "2026-02-05-tui-redesign.md", "status": "completed" },
|
{
|
||||||
{ "file": "2026-02-05-tui-redesign-implementation.md", "status": "completed" },
|
"file": "2026-02-02-flynn-phase1-implementation.md",
|
||||||
{ "file": "2026-02-05-llamacpp-integration-design.md", "status": "completed" },
|
"status": "completed"
|
||||||
{ "file": "2026-02-05-llamacpp-implementation.md", "status": "completed" },
|
},
|
||||||
{ "file": "2026-02-05-backend-switch-design.md", "status": "completed" },
|
{
|
||||||
{ "file": "2026-02-05-backend-switch-implementation.md", "status": "completed" },
|
"file": "2026-02-02-flynn-phase2-implementation.md",
|
||||||
{ "file": "2026-02-05-openclaw-parity-design.md", "status": "completed" },
|
"status": "completed"
|
||||||
{ "file": "2026-02-05-phase1-tool-framework.md", "status": "completed" },
|
},
|
||||||
{ "file": "2026-02-05-phase2-websocket-gateway.md", "status": "completed" },
|
{
|
||||||
{ "file": "2026-02-05-phase3-channel-adapters.md", "status": "completed" },
|
"file": "2026-02-05-flynn-phase3-implementation.md",
|
||||||
{ "file": "2026-02-05-phase5-cli-cron-doctor-design.md", "status": "completed" },
|
"status": "completed"
|
||||||
{ "file": "2026-02-05-phase5a-implementation.md", "status": "completed" }
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-tui-redesign.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-tui-redesign-implementation.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-llamacpp-integration-design.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-llamacpp-implementation.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-backend-switch-design.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-backend-switch-implementation.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-openclaw-parity-design.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-phase1-tool-framework.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-phase2-websocket-gateway.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-phase3-channel-adapters.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-phase5-cli-cron-doctor-design.md",
|
||||||
|
"status": "completed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "2026-02-05-phase5a-implementation.md",
|
||||||
|
"status": "completed"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"audit-hardening-xss-body-limits-timeout-leak": {
|
||||||
|
"status": "completed",
|
||||||
|
"date": "2026-02-16",
|
||||||
|
"updated": "2026-02-16",
|
||||||
|
"summary": "Addressed high-priority codebase audit findings by hardening gateway chat markdown rendering (sanitization before DOM insertion), adding configurable inbound HTTP body-size limits for gateway/webhooks with 413 responses, centralizing body parsing utility, and fixing ToolExecutor timeout timer cleanup to avoid orphaned timers.",
|
||||||
|
"files_created": [
|
||||||
|
"src/gateway/ui/lib/markdown.js",
|
||||||
|
"src/gateway/ui/lib/markdown.test.ts",
|
||||||
|
"src/utils/httpBody.ts",
|
||||||
|
"src/utils/httpBody.test.ts"
|
||||||
|
],
|
||||||
|
"files_modified": [
|
||||||
|
"src/gateway/ui/pages/chat.js",
|
||||||
|
"src/gateway/ui/chat.html",
|
||||||
|
"src/gateway/server.ts",
|
||||||
|
"src/gateway/server.test.ts",
|
||||||
|
"src/automation/webhooks.ts",
|
||||||
|
"src/automation/webhooks.test.ts",
|
||||||
|
"src/config/schema.ts",
|
||||||
|
"src/config/schema.test.ts",
|
||||||
|
"config/default.yaml",
|
||||||
|
"src/daemon/services.ts",
|
||||||
|
"src/daemon/channels.ts",
|
||||||
|
"src/tools/executor.ts",
|
||||||
|
"src/tools/executor.test.ts",
|
||||||
|
"README.md",
|
||||||
|
"docs/deployment/PRODUCTION.md"
|
||||||
|
],
|
||||||
|
"test_status": "targeted: pnpm test:run src/gateway/server.test.ts src/automation/webhooks.test.ts src/tools/executor.test.ts src/config/schema.test.ts src/gateway/ui/lib/markdown.test.ts src/utils/httpBody.test.ts + pnpm typecheck"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"overall_progress": {
|
"overall_progress": {
|
||||||
"total_test_count": 1703,
|
"total_test_count": 1703,
|
||||||
"all_tests_passing": true,
|
"all_tests_passing": true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
import { ToolExecutor } from './executor.js';
|
import { ToolExecutor } from './executor.js';
|
||||||
import { ToolRegistry } from './registry.js';
|
import { ToolRegistry } from './registry.js';
|
||||||
import { HookEngine } from '../hooks/engine.js';
|
import { HookEngine } from '../hooks/engine.js';
|
||||||
@@ -98,6 +98,22 @@ describe('ToolExecutor', () => {
|
|||||||
expect(result.output).toContain('[truncated]');
|
expect(result.output).toContain('[truncated]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('clears timeout timer after fast tool completion', async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
try {
|
||||||
|
const registry = new ToolRegistry();
|
||||||
|
registry.register(echoTool);
|
||||||
|
const hooks = new HookEngine({ confirm: [], log: [], silent: [] });
|
||||||
|
const executor = new ToolExecutor(registry, hooks, { defaultTimeoutMs: 30_000 });
|
||||||
|
|
||||||
|
const result = await executor.execute('test.echo', { text: 'hello' });
|
||||||
|
expect(result.success).toBe(true);
|
||||||
|
expect(vi.getTimerCount()).toBe(0);
|
||||||
|
} finally {
|
||||||
|
vi.useRealTimers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('blocks on confirm hook and resolves when approved', async () => {
|
it('blocks on confirm hook and resolves when approved', async () => {
|
||||||
const registry = new ToolRegistry();
|
const registry = new ToolRegistry();
|
||||||
registry.register(echoTool);
|
registry.register(echoTool);
|
||||||
|
|||||||
+11
-3
@@ -224,6 +224,7 @@ export class ToolExecutor {
|
|||||||
agent_tier: context?.tier,
|
agent_tier: context?.tier,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let timeoutHandle: NodeJS.Timeout | undefined;
|
||||||
try {
|
try {
|
||||||
const result = await Promise.race([
|
const result = await Promise.race([
|
||||||
(async () => {
|
(async () => {
|
||||||
@@ -239,9 +240,12 @@ export class ToolExecutor {
|
|||||||
}
|
}
|
||||||
return tool.execute(args);
|
return tool.execute(args);
|
||||||
})(),
|
})(),
|
||||||
new Promise<ToolResult>((_, reject) =>
|
new Promise<ToolResult>((_, reject) => {
|
||||||
setTimeout(() => reject(new Error(`Tool '${toolName}' timed out after ${this.defaultTimeoutMs}ms`)), this.defaultTimeoutMs),
|
timeoutHandle = setTimeout(
|
||||||
),
|
() => reject(new Error(`Tool '${toolName}' timed out after ${this.defaultTimeoutMs}ms`)),
|
||||||
|
this.defaultTimeoutMs,
|
||||||
|
);
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
@@ -286,6 +290,10 @@ export class ToolExecutor {
|
|||||||
output: '',
|
output: '',
|
||||||
error: String(errorRedaction.value),
|
error: String(errorRedaction.value),
|
||||||
};
|
};
|
||||||
|
} finally {
|
||||||
|
if (timeoutHandle) {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user