clawmini 0.0.7 → 0.0.9
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/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.github/workflows/release.yml +49 -0
- package/CHANGELOG.md +36 -0
- package/README.md +5 -4
- package/dist/adapter-discord/index.d.mts.map +1 -1
- package/dist/adapter-discord/index.mjs +465 -282
- package/dist/adapter-discord/index.mjs.map +1 -1
- package/dist/adapter-google-chat/index.mjs +367 -243
- package/dist/adapter-google-chat/index.mjs.map +1 -1
- package/dist/cli/index.mjs +684 -24
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lite.mjs +43 -13
- package/dist/cli/lite.mjs.map +1 -1
- package/dist/cli/{propose-policy.mjs → manage-policies.mjs} +270 -47
- package/dist/cli/manage-policies.mjs.map +1 -0
- package/dist/cli/run-host.d.mts +1 -0
- package/dist/cli/run-host.mjs +3090 -0
- package/dist/cli/run-host.mjs.map +1 -0
- package/dist/config-CPFQIGdG.mjs +57 -0
- package/dist/config-CPFQIGdG.mjs.map +1 -0
- package/dist/config-Dvl-Pov4.mjs +76 -0
- package/dist/config-Dvl-Pov4.mjs.map +1 -0
- package/dist/daemon/index.d.mts.map +1 -1
- package/dist/daemon/index.mjs +970 -332
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/supervisor-actions-CiW56eLi.mjs +843 -0
- package/dist/supervisor-actions-CiW56eLi.mjs.map +1 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs +767 -0
- package/dist/turn-log-buffer-DRgW53gl.mjs.map +1 -0
- package/dist/web/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/dist/web/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/dist/web/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/CME08kGM.js → dist/web/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/dist/web/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/dist/web/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/dist/web/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/dist/web/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/dist/web/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/dist/web/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{web/.svelte-kit/output/client/_app/immutable/chunks/Ck-be5J2.js → dist/web/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/dist/web/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/dist/web/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/dist/web/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/dist/web/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/dist/web/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{web/.svelte-kit/output/client/_app/immutable/nodes/3.Dr0ot9sV.js → dist/web/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/dist/web/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/dist/web/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +12 -12
- package/dist/workspace-oWmVh5mi.mjs +1001 -0
- package/dist/workspace-oWmVh5mi.mjs.map +1 -0
- package/docs/23_adapter_slash_autocomplete/development_log.md +19 -0
- package/docs/23_adapter_slash_autocomplete/notes.md +18 -0
- package/docs/23_adapter_slash_autocomplete/prd.md +46 -0
- package/docs/23_adapter_slash_autocomplete/questions.md +6 -0
- package/docs/23_adapter_slash_autocomplete/tickets.md +21 -0
- package/docs/24_subagent_job_policy_fixes/development_log.md +22 -0
- package/docs/24_subagent_job_policy_fixes/notes.md +28 -0
- package/docs/24_subagent_job_policy_fixes/prd.md +59 -0
- package/docs/24_subagent_job_policy_fixes/questions.md +3 -0
- package/docs/24_subagent_job_policy_fixes/tickets.md +49 -0
- package/docs/25_e2e_test_improvements/development_log.md +30 -0
- package/docs/25_e2e_test_improvements/notes.md +29 -0
- package/docs/25_e2e_test_improvements/prd.md +43 -0
- package/docs/25_e2e_test_improvements/questions.md +12 -0
- package/docs/25_e2e_test_improvements/tickets-2.md +22 -0
- package/docs/25_e2e_test_improvements/tickets.md +22 -0
- package/docs/25_policy_cwd/development_log.md +30 -0
- package/docs/25_policy_cwd/notes.md +28 -0
- package/docs/25_policy_cwd/prd.md +77 -0
- package/docs/25_policy_cwd/questions.md +6 -0
- package/docs/25_policy_cwd/tickets.md +77 -0
- package/docs/CLI_REFERENCE.md +3 -1
- package/docs/PHILOSOPHY.md +35 -0
- package/docs/adapter-visibility/SPEC.md +461 -0
- package/docs/adapter-visibility/SPEC_v2.md +202 -0
- package/docs/auto-update/SPEC.md +344 -0
- package/docs/backups/SPEC.md +296 -0
- package/docs/backups/clawmini.gitignore +69 -0
- package/docs/guides/assets/clawmini-avatar.png +0 -0
- package/docs/guides/backups.md +332 -0
- package/docs/guides/discord_adapter_setup.md +1 -1
- package/docs/guides/google_chat_adapter_setup.md +81 -0
- package/docs/unified-startup/SPEC.md +203 -0
- package/e2e/_helpers/test-environment.test.ts +49 -0
- package/e2e/_helpers/test-environment.ts +548 -0
- package/e2e/adapters/_google-chat-fixtures.ts +340 -0
- package/{src/cli/e2e → e2e/adapters}/adapter-discord.test.ts +22 -23
- package/e2e/adapters/adapter-google-chat-downtime.test.ts +157 -0
- package/e2e/adapters/adapter-google-chat-inbound.test.ts +697 -0
- package/e2e/adapters/adapter-google-chat-outbound.test.ts +297 -0
- package/e2e/adapters/adapter-google-chat-roundtrip.test.ts +56 -0
- package/e2e/adapters/adapter-google-chat-threads.test.ts +1078 -0
- package/e2e/agents/custom-api-env.test.ts +80 -0
- package/e2e/agents/export-lite-func.test.ts +104 -0
- package/e2e/agents/fallbacks.test.ts +124 -0
- package/e2e/agents/interrupt.test.ts +50 -0
- package/e2e/agents/no-reply-necessary.test.ts +57 -0
- package/e2e/agents/session-timeout-subagents.test.ts +76 -0
- package/e2e/agents/subagent-authorization.test.ts +246 -0
- package/e2e/agents/subagent-env.test.ts +49 -0
- package/e2e/agents/subagent-lifecycle.test.ts +782 -0
- package/e2e/agents/subagents-depth.test.ts +47 -0
- package/e2e/cli/agents.test.ts +176 -0
- package/e2e/cli/auto-update.test.ts +741 -0
- package/e2e/cli/basic.test.ts +44 -0
- package/{src/cli/e2e → e2e/cli}/export-lite.test.ts +16 -12
- package/e2e/cli/init-gitignore.test.ts +86 -0
- package/e2e/cli/init.test.ts +76 -0
- package/e2e/cli/messages.test.ts +363 -0
- package/e2e/cli/serve.test.ts +76 -0
- package/{src/cli/e2e → e2e/cli}/skills.test.ts +11 -10
- package/{src/cli/e2e → e2e/daemon}/daemon.test.ts +57 -195
- package/e2e/jobs/agent-jobs.test.ts +216 -0
- package/e2e/jobs/cron.test.ts +64 -0
- package/e2e/jobs/restart.test.ts +108 -0
- package/e2e/policies/approval-session.test.ts +69 -0
- package/e2e/policies/auto-create-policies-file.test.ts +35 -0
- package/e2e/policies/builtin-manage-policies.test.ts +184 -0
- package/e2e/policies/builtin-run-host.test.ts +180 -0
- package/e2e/policies/environment-policies.test.ts +177 -0
- package/e2e/policies/manage-policies.test.ts +566 -0
- package/e2e/policies/output-size.test.ts +98 -0
- package/e2e/policies/policies-context-cwd.test.ts +160 -0
- package/e2e/policies/relative-script-path.test.ts +60 -0
- package/e2e/policies/requests-show.test.ts +135 -0
- package/e2e/policies/requests.test.ts +208 -0
- package/e2e/policies/slash-policies.test.ts +308 -0
- package/e2e/policies/startup-cleanup.test.ts +48 -0
- package/e2e/routers/session-timeout.test.ts +106 -0
- package/e2e/routers/slash-model.test.ts +152 -0
- package/e2e/routers/slash-new.test.ts +50 -0
- package/e2e/routers/slash-restart-adapter.test.ts +96 -0
- package/e2e/routers/slash-restart.test.ts +114 -0
- package/e2e/routers/slash-shutdown.test.ts +55 -0
- package/e2e/routers/slash-stop.test.ts +232 -0
- package/e2e/routers/slash-upgrade.test.ts +88 -0
- package/{src/cli/e2e → e2e/sandbox}/environments.test.ts +14 -13
- package/eslint.config.js +6 -0
- package/napkin.md +1 -1
- package/package.json +8 -3
- package/src/adapter-discord/commands.test.ts +42 -0
- package/src/adapter-discord/commands.ts +33 -0
- package/src/adapter-discord/config.ts +12 -0
- package/src/adapter-discord/forwarder.test.ts +499 -21
- package/src/adapter-discord/forwarder.ts +343 -124
- package/src/adapter-discord/inbound-cache.test.ts +47 -0
- package/src/adapter-discord/inbound-cache.ts +37 -0
- package/src/adapter-discord/index.test.ts +67 -2
- package/src/adapter-discord/index.ts +84 -216
- package/src/adapter-discord/interactions.test.ts +54 -3
- package/src/adapter-discord/interactions.ts +97 -53
- package/src/adapter-discord/processMessage.ts +239 -0
- package/src/adapter-discord/state.ts +1 -0
- package/src/adapter-google-chat/auth.test.ts +9 -5
- package/src/adapter-google-chat/auth.ts +29 -23
- package/src/adapter-google-chat/cards.ts +7 -2
- package/src/adapter-google-chat/client.test.ts +37 -2
- package/src/adapter-google-chat/client.ts +138 -38
- package/src/adapter-google-chat/config.ts +19 -0
- package/src/adapter-google-chat/forwarder.test.ts +81 -56
- package/src/adapter-google-chat/forwarder.ts +394 -185
- package/src/adapter-google-chat/inbound-cache.test.ts +61 -0
- package/src/adapter-google-chat/inbound-cache.ts +36 -0
- package/src/adapter-google-chat/state.test.ts +1 -0
- package/src/adapter-google-chat/state.ts +9 -1
- package/src/adapter-google-chat/subscriptions.ts +8 -6
- package/src/cli/builtin-policies.ts +44 -0
- package/src/cli/commands/agents.ts +59 -5
- package/src/cli/commands/down.ts +54 -2
- package/src/cli/commands/environments.ts +8 -2
- package/src/cli/commands/init.ts +31 -0
- package/src/cli/commands/logs.ts +116 -0
- package/src/cli/commands/policies.ts +6 -4
- package/src/cli/commands/serve.test.ts +67 -0
- package/src/cli/commands/serve.ts +284 -0
- package/src/cli/commands/up.ts +122 -2
- package/src/cli/commands/web-api/agents.ts +3 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/install-detection.test.ts +72 -0
- package/src/cli/install-detection.ts +48 -0
- package/src/cli/lite.ts +54 -22
- package/src/cli/manage-policies-utils.ts +104 -0
- package/src/cli/manage-policies.ts +291 -0
- package/src/cli/run-host.ts +45 -0
- package/src/cli/supervisor-actions.ts +267 -0
- package/src/cli/supervisor-control.test.ts +129 -0
- package/src/cli/supervisor-control.ts +155 -0
- package/src/cli/supervisor-pid.ts +68 -0
- package/src/cli/supervisor.ts +277 -0
- package/src/daemon/agent/agent-context.ts +11 -11
- package/src/daemon/agent/agent-session.ts +8 -1
- package/src/daemon/agent/chat-logger.test.ts +78 -9
- package/src/daemon/agent/chat-logger.ts +25 -5
- package/src/daemon/agent/turn-registry.test.ts +89 -0
- package/src/daemon/agent/turn-registry.ts +94 -0
- package/src/daemon/agent/types.ts +2 -0
- package/src/daemon/api/agent-policy-endpoints.ts +263 -0
- package/src/daemon/api/agent-router.ts +47 -126
- package/src/daemon/api/index.test.ts +1 -0
- package/src/daemon/api/policy-request.test.ts +7 -5
- package/src/daemon/api/router-utils.ts +6 -5
- package/src/daemon/api/subagent-router.ts +110 -74
- package/src/daemon/api/subagent-utils.test.ts +60 -0
- package/src/daemon/api/subagent-utils.ts +113 -87
- package/src/daemon/api/user-router.ts +34 -8
- package/src/daemon/auth.ts +1 -0
- package/src/daemon/cron.test.ts +62 -4
- package/src/daemon/cron.ts +42 -16
- package/src/daemon/events.ts +65 -0
- package/src/daemon/index.ts +24 -1
- package/src/daemon/message-interruption.test.ts +1 -0
- package/src/daemon/message-jobs.test.ts +1 -0
- package/src/daemon/message.ts +78 -14
- package/src/daemon/observation.test.ts +26 -18
- package/src/daemon/pending-replies.test.ts +112 -0
- package/src/daemon/pending-replies.ts +162 -0
- package/src/daemon/policy-request-service.ts +3 -1
- package/src/daemon/policy-utils.test.ts +66 -1
- package/src/daemon/policy-utils.ts +126 -1
- package/src/daemon/request-store.ts +31 -0
- package/src/daemon/routers/session-timeout.ts +4 -0
- package/src/daemon/routers/slash-model.test.ts +344 -0
- package/src/daemon/routers/slash-model.ts +207 -0
- package/src/daemon/routers/slash-policies.test.ts +38 -32
- package/src/daemon/routers/slash-policies.ts +84 -33
- package/src/daemon/routers/slash-restart.test.ts +69 -0
- package/src/daemon/routers/slash-restart.ts +36 -0
- package/src/daemon/routers/slash-shutdown.test.ts +50 -0
- package/src/daemon/routers/slash-shutdown.ts +28 -0
- package/src/daemon/routers/slash-upgrade.test.ts +116 -0
- package/src/daemon/routers/slash-upgrade.ts +76 -0
- package/src/daemon/routers/types.ts +7 -0
- package/src/daemon/routers.ts +16 -0
- package/src/shared/adapters/blockquote.test.ts +28 -0
- package/src/shared/adapters/blockquote.ts +20 -0
- package/src/shared/adapters/filtering.test.ts +224 -10
- package/src/shared/adapters/filtering.ts +95 -7
- package/src/shared/adapters/inbound-cache.test.ts +48 -0
- package/src/shared/adapters/inbound-cache.ts +54 -0
- package/src/shared/adapters/turn-log-buffer.ts +266 -0
- package/src/shared/adapters/turn-log.test.ts +389 -0
- package/src/shared/adapters/turn-log.ts +357 -0
- package/src/shared/agent-utils.ts +12 -5
- package/src/shared/chats.test.ts +4 -0
- package/src/shared/chats.ts +9 -0
- package/src/shared/config.ts +16 -1
- package/src/shared/lite.ts +76 -2
- package/src/shared/policies.ts +26 -0
- package/src/shared/template-manifest.ts +267 -0
- package/src/shared/utils/shell.ts +61 -0
- package/src/shared/version.ts +34 -0
- package/src/shared/workspace.test.ts +217 -0
- package/src/shared/workspace.ts +626 -48
- package/templates/environments/cladding/allowlist-domain.mjs +125 -0
- package/templates/environments/cladding/env.json +21 -1
- package/templates/environments/cladding/run-with-network.mjs +54 -0
- package/templates/environments/macos-proxy/allowlist-domain.mjs +95 -0
- package/templates/environments/macos-proxy/env.json +8 -1
- package/templates/environments/macos-proxy/proxy.mjs +42 -13
- package/templates/gemini/template.json +5 -0
- package/templates/gemini-claw/template.json +13 -0
- package/templates/skills/clawmini-requests/SKILL.md +69 -10
- package/templates/skills/run-host/SKILL.md +51 -0
- package/templates/skills/skill-creator/SKILL.md +4 -3
- package/templates/skills/skill-creator/scripts/validate.sh +52 -0
- package/tsdown.config.ts +10 -1
- package/vitest.config.ts +2 -2
- package/web/.svelte-kit/ambient.d.ts +292 -176
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +127 -137
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Drm9vgeP.js → 3AZlWB6U.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BhRSsUCh.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BiLeM2i1.js +1 -0
- package/{dist/web/_app/immutable/chunks/CME08kGM.js → web/.svelte-kit/output/client/_app/immutable/chunks/BmBj85Ll.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BrERcKAH.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Bv9252RM.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CIXNBPKi.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DISKL3GN.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{Zeh-C-mx.js → DcpaLzmX.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DnQ3vS13.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/chunks/KsloHTKS.js +1 -0
- package/{dist/web/_app/immutable/chunks/Ck-be5J2.js → web/.svelte-kit/output/client/_app/immutable/chunks/RsHsUj-8.js} +2 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/{G_zz-Gou.js → wpfV79dV.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CIw1Qj0n.js +2 -0
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.Di0-Jhte.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{0.CYS8iApT.js → 0.DYyUA1au.js} +1 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.D-3QEMMZ.js +1 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{2.BnwnD1Ki.js → 2.4olHnH7U.js} +1 -1
- package/{dist/web/_app/immutable/nodes/3.Dr0ot9sV.js → web/.svelte-kit/output/client/_app/immutable/nodes/3.4w0bE-m2.js} +3 -3
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.CZvjhVHt.js +60 -0
- package/web/.svelte-kit/output/client/_app/immutable/nodes/{5.BBGQ_i84.js → 5.DLbPVJY2.js} +1 -1
- package/web/.svelte-kit/output/client/_app/version.json +1 -1
- package/web/.svelte-kit/output/server/.vite/manifest.json +12 -10
- package/web/.svelte-kit/output/server/chunks/Icon.js +1 -1
- package/web/.svelte-kit/output/server/chunks/client.js +1 -1
- package/web/.svelte-kit/output/server/chunks/exports.js +1 -1
- package/web/.svelte-kit/output/server/chunks/index-server.js +2 -1
- package/web/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/web/.svelte-kit/output/server/chunks/render-context.js +77 -0
- package/web/.svelte-kit/output/server/chunks/root.js +739 -788
- package/web/.svelte-kit/output/server/chunks/shared.js +234 -21
- package/web/.svelte-kit/output/server/index.js +126 -90
- package/web/.svelte-kit/output/server/manifest-full.js +1 -1
- package/web/.svelte-kit/output/server/manifest.js +1 -1
- package/web/.svelte-kit/output/server/nodes/0.js +1 -1
- package/web/.svelte-kit/output/server/nodes/1.js +1 -1
- package/web/.svelte-kit/output/server/nodes/2.js +1 -1
- package/web/.svelte-kit/output/server/nodes/3.js +1 -1
- package/web/.svelte-kit/output/server/nodes/4.js +1 -1
- package/web/.svelte-kit/output/server/nodes/5.js +1 -1
- package/web/.svelte-kit/output/server/remote-entry.js +245 -81
- package/web/.svelte-kit/tsconfig.json +4 -1
- package/dist/cli/propose-policy.mjs.map +0 -1
- package/dist/lite-CBxOT1y5.mjs +0 -241
- package/dist/lite-CBxOT1y5.mjs.map +0 -1
- package/dist/routing-D8rTxtaV.mjs +0 -245
- package/dist/routing-D8rTxtaV.mjs.map +0 -1
- package/dist/web/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/dist/web/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/dist/web/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/dist/web/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/dist/web/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/dist/web/_app/immutable/chunks/DhD271EB.js +0 -1
- package/dist/web/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/dist/web/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/dist/web/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/dist/web/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/dist/web/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/dist/web/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/dist/web/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/dist/workspace-BJmJBfKi.mjs +0 -456
- package/dist/workspace-BJmJBfKi.mjs.map +0 -1
- package/src/cli/e2e/agents.test.ts +0 -140
- package/src/cli/e2e/basic.test.ts +0 -43
- package/src/cli/e2e/cron.test.ts +0 -132
- package/src/cli/e2e/export-lite-func.test.ts +0 -206
- package/src/cli/e2e/fallbacks.test.ts +0 -175
- package/src/cli/e2e/init.test.ts +0 -77
- package/src/cli/e2e/messages.test.ts +0 -332
- package/src/cli/e2e/propose-policy.test.ts +0 -203
- package/src/cli/e2e/requests.test.ts +0 -180
- package/src/cli/e2e/session-timeout.test.ts +0 -192
- package/src/cli/e2e/slash-new.test.ts +0 -93
- package/src/cli/e2e/subagents.test.ts +0 -106
- package/src/cli/e2e/utils.ts +0 -66
- package/src/cli/propose-policy.ts +0 -91
- package/web/.svelte-kit/output/client/_app/immutable/chunks/B6YN0Nuq.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/BmRlVmv6.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/CK9JZLaG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/Ck3rYNON.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DMtIqaiV.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DhD271EB.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DpuLqk8d.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/DsIToJCP.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/chunks/bBmtyQMj.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/entry/app.CJmSwntr.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.ZpUrT2ak.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.Bli0Hqzn.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.oBhvQhcA.js +0 -60
- package/web/.svelte-kit/output/server/chunks/false.js +0 -4
- /package/dist/cli/{propose-policy.d.mts → manage-policies.d.mts} +0 -0
- /package/{src/cli/e2e → e2e/_helpers}/global-setup.ts +0 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type { ChildProcess } from 'node:child_process';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import readline from 'node:readline';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
import { getSocketPath } from '../shared/workspace.js';
|
|
9
|
+
|
|
10
|
+
export type ServiceName = 'daemon' | 'web' | 'adapter-discord' | 'adapter-google-chat';
|
|
11
|
+
|
|
12
|
+
export const DISPLAY_NAMES: Record<ServiceName, string> = {
|
|
13
|
+
daemon: 'daemon',
|
|
14
|
+
web: 'web',
|
|
15
|
+
'adapter-discord': 'discord',
|
|
16
|
+
'adapter-google-chat': 'google-chat',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// Adapters and the web UI are mostly stateless — give them a tight window.
|
|
20
|
+
// The daemon runs `down` hooks (e.g. sandbox/container teardown) that can
|
|
21
|
+
// legitimately take tens of seconds, so it needs much longer to drain.
|
|
22
|
+
const ADAPTER_TERMINATE_TIMEOUT_MS = 10_000;
|
|
23
|
+
const DAEMON_TERMINATE_TIMEOUT_MS = 60_000;
|
|
24
|
+
|
|
25
|
+
interface ResolvedCommand {
|
|
26
|
+
command: string;
|
|
27
|
+
args: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveServiceCommand(service: ServiceName): ResolvedCommand {
|
|
31
|
+
const cliPath = fileURLToPath(import.meta.url);
|
|
32
|
+
switch (service) {
|
|
33
|
+
case 'daemon':
|
|
34
|
+
return {
|
|
35
|
+
command: process.execPath,
|
|
36
|
+
args: [new URL('../daemon/index.mjs', import.meta.url).pathname],
|
|
37
|
+
};
|
|
38
|
+
case 'web':
|
|
39
|
+
return { command: process.execPath, args: [cliPath, 'web'] };
|
|
40
|
+
case 'adapter-discord':
|
|
41
|
+
return {
|
|
42
|
+
command: process.execPath,
|
|
43
|
+
args: [new URL('../adapter-discord/index.mjs', import.meta.url).pathname],
|
|
44
|
+
};
|
|
45
|
+
case 'adapter-google-chat':
|
|
46
|
+
return {
|
|
47
|
+
command: process.execPath,
|
|
48
|
+
args: [new URL('../adapter-google-chat/index.mjs', import.meta.url).pathname],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class Supervisor {
|
|
54
|
+
private children = new Map<ServiceName, ChildProcess>();
|
|
55
|
+
private logFds = new Map<ServiceName, number>();
|
|
56
|
+
private shuttingDown = false;
|
|
57
|
+
private restarting = new Set<ServiceName>();
|
|
58
|
+
// Services that have ever been started in this supervisor's lifetime. Used
|
|
59
|
+
// by restartAll() to know what to bring back after a stopAllChildren().
|
|
60
|
+
// We don't remove entries when a service stops — a crash-and-restart of an
|
|
61
|
+
// adapter shouldn't drop it from the "originally enabled" set.
|
|
62
|
+
private enabledServices = new Set<ServiceName>();
|
|
63
|
+
private readonly logDir: string;
|
|
64
|
+
|
|
65
|
+
constructor(logDir: string) {
|
|
66
|
+
this.logDir = logDir;
|
|
67
|
+
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async startService(service: ServiceName): Promise<void> {
|
|
71
|
+
this.enabledServices.add(service);
|
|
72
|
+
const { command, args } = resolveServiceCommand(service);
|
|
73
|
+
const logPath = path.join(this.logDir, `${service}.log`);
|
|
74
|
+
const logFd = fs.openSync(logPath, 'a');
|
|
75
|
+
this.logFds.set(service, logFd);
|
|
76
|
+
|
|
77
|
+
fs.writeSync(
|
|
78
|
+
logFd,
|
|
79
|
+
`\n--- clawmini serve: ${service} starting at ${new Date().toISOString()} ---\n`
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const child = spawn(command, args, {
|
|
83
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
84
|
+
env: process.env,
|
|
85
|
+
cwd: process.cwd(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
this.children.set(service, child);
|
|
89
|
+
this.attachPipe(service, child.stdout!, logFd, 'stdout');
|
|
90
|
+
this.attachPipe(service, child.stderr!, logFd, 'stderr');
|
|
91
|
+
|
|
92
|
+
child.on('exit', (code, signal) => {
|
|
93
|
+
const msg = `exited code=${code} signal=${signal}`;
|
|
94
|
+
process.stderr.write(`[${DISPLAY_NAMES[service]}] ${msg}\n`);
|
|
95
|
+
try {
|
|
96
|
+
fs.writeSync(logFd, `--- ${msg} at ${new Date().toISOString()} ---\n`);
|
|
97
|
+
fs.closeSync(logFd);
|
|
98
|
+
} catch {
|
|
99
|
+
// best-effort
|
|
100
|
+
}
|
|
101
|
+
this.logFds.delete(service);
|
|
102
|
+
this.children.delete(service);
|
|
103
|
+
|
|
104
|
+
if (service === 'daemon' && !this.shuttingDown && !this.restarting.has('daemon')) {
|
|
105
|
+
process.stderr.write('[supervisor] daemon exited unexpectedly — shutting down\n');
|
|
106
|
+
void this.shutdown(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private attachPipe(
|
|
112
|
+
service: ServiceName,
|
|
113
|
+
stream: NodeJS.ReadableStream,
|
|
114
|
+
logFd: number,
|
|
115
|
+
kind: 'stdout' | 'stderr'
|
|
116
|
+
): void {
|
|
117
|
+
const prefix = `[${DISPLAY_NAMES[service]}] `;
|
|
118
|
+
const target = kind === 'stderr' ? process.stderr : process.stdout;
|
|
119
|
+
const rl = readline.createInterface({ input: stream });
|
|
120
|
+
rl.on('line', (line) => {
|
|
121
|
+
try {
|
|
122
|
+
fs.writeSync(logFd, line + '\n');
|
|
123
|
+
} catch {
|
|
124
|
+
// best-effort: logs on disk are not critical
|
|
125
|
+
}
|
|
126
|
+
target.write(prefix + line + '\n');
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async waitForDaemonSocket(timeoutMs = 10_000): Promise<void> {
|
|
131
|
+
const socketPath = getSocketPath();
|
|
132
|
+
const start = Date.now();
|
|
133
|
+
while (Date.now() - start < timeoutMs) {
|
|
134
|
+
if (fs.existsSync(socketPath)) return;
|
|
135
|
+
if (!this.children.has('daemon')) {
|
|
136
|
+
throw new Error('Daemon exited before socket became available.');
|
|
137
|
+
}
|
|
138
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`Daemon did not start within ${timeoutMs}ms`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async shutdown(exitCode = 0): Promise<void> {
|
|
144
|
+
if (this.shuttingDown) return;
|
|
145
|
+
this.shuttingDown = true;
|
|
146
|
+
process.stderr.write('\n[supervisor] shutting down...\n');
|
|
147
|
+
|
|
148
|
+
await this.stopAllChildren();
|
|
149
|
+
|
|
150
|
+
process.exit(exitCode);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Stop all children and close log fds without exiting the process. Used by
|
|
155
|
+
* `/upgrade`, which needs to tear down everything, run an install, then
|
|
156
|
+
* launch a fresh supervisor.
|
|
157
|
+
*/
|
|
158
|
+
async stopAllChildren(): Promise<void> {
|
|
159
|
+
// Phase 1: stop adapters and the web UI in parallel. They depend on the
|
|
160
|
+
// daemon, so taking them down first lets the daemon's `down` hooks run
|
|
161
|
+
// without interference from disconnect noise.
|
|
162
|
+
const adapterStops: Promise<void>[] = [];
|
|
163
|
+
for (const [name, child] of this.children) {
|
|
164
|
+
if (name === 'daemon') continue;
|
|
165
|
+
adapterStops.push(Supervisor.terminateChild(name, child, ADAPTER_TERMINATE_TIMEOUT_MS));
|
|
166
|
+
}
|
|
167
|
+
await Promise.allSettled(adapterStops);
|
|
168
|
+
|
|
169
|
+
// Phase 2: stop the daemon with a generous timeout so its `down` hooks
|
|
170
|
+
// (sandbox/container teardown) can complete.
|
|
171
|
+
const daemonChild = this.children.get('daemon');
|
|
172
|
+
if (daemonChild) {
|
|
173
|
+
await Supervisor.terminateChild('daemon', daemonChild, DAEMON_TERMINATE_TIMEOUT_MS);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const fd of this.logFds.values()) {
|
|
177
|
+
try {
|
|
178
|
+
fs.closeSync(fd);
|
|
179
|
+
} catch {
|
|
180
|
+
// best-effort
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
this.logFds.clear();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Bounce every service that has ever been started under this supervisor.
|
|
188
|
+
* Daemon goes down with the adapters, then comes back first so the
|
|
189
|
+
* adapters can re-establish their tRPC subscriptions to the new daemon.
|
|
190
|
+
*
|
|
191
|
+
* This is what `/restart` and the `/upgrade` failure recovery paths call:
|
|
192
|
+
* just bouncing the daemon would leave adapter-discord (and any other
|
|
193
|
+
* adapter) holding a dead subscription, so outbound messages would never
|
|
194
|
+
* reach the chat. The user-visible symptom was "I send /restart and then
|
|
195
|
+
* the daemon's reply never shows up in Discord."
|
|
196
|
+
*/
|
|
197
|
+
async restartAll(): Promise<void> {
|
|
198
|
+
if (this.shuttingDown) return;
|
|
199
|
+
// Suppress the daemon's unexpected-exit guard while we tear it down.
|
|
200
|
+
this.restarting.add('daemon');
|
|
201
|
+
try {
|
|
202
|
+
await this.stopAllChildren();
|
|
203
|
+
// Wait for the exit handlers to drain bookkeeping.
|
|
204
|
+
await new Promise((r) => setImmediate(r));
|
|
205
|
+
|
|
206
|
+
if (this.enabledServices.has('daemon')) {
|
|
207
|
+
await this.startService('daemon');
|
|
208
|
+
await this.waitForDaemonSocket();
|
|
209
|
+
}
|
|
210
|
+
for (const name of this.enabledServices) {
|
|
211
|
+
if (name === 'daemon') continue;
|
|
212
|
+
await this.startService(name);
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
this.restarting.delete('daemon');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Stop and re-spawn a single service. The exit handler is suppressed so a
|
|
221
|
+
* restarted daemon doesn't trigger a full shutdown.
|
|
222
|
+
*/
|
|
223
|
+
async restartService(service: ServiceName): Promise<void> {
|
|
224
|
+
if (this.shuttingDown) return;
|
|
225
|
+
this.restarting.add(service);
|
|
226
|
+
try {
|
|
227
|
+
const child = this.children.get(service);
|
|
228
|
+
if (child) {
|
|
229
|
+
const timeoutMs =
|
|
230
|
+
service === 'daemon' ? DAEMON_TERMINATE_TIMEOUT_MS : ADAPTER_TERMINATE_TIMEOUT_MS;
|
|
231
|
+
await Supervisor.terminateChild(service, child, timeoutMs);
|
|
232
|
+
}
|
|
233
|
+
// Wait until the exit handler has cleared bookkeeping. The handler runs
|
|
234
|
+
// synchronously on the same tick as `exit`, so a microtask hop is enough.
|
|
235
|
+
await new Promise((r) => setImmediate(r));
|
|
236
|
+
await this.startService(service);
|
|
237
|
+
if (service === 'daemon') {
|
|
238
|
+
await this.waitForDaemonSocket();
|
|
239
|
+
}
|
|
240
|
+
} finally {
|
|
241
|
+
this.restarting.delete(service);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private static terminateChild(
|
|
246
|
+
name: ServiceName,
|
|
247
|
+
child: ChildProcess,
|
|
248
|
+
timeoutMs: number
|
|
249
|
+
): Promise<void> {
|
|
250
|
+
return new Promise((resolve) => {
|
|
251
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
252
|
+
resolve();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const timer = setTimeout(() => {
|
|
256
|
+
process.stderr.write(
|
|
257
|
+
`[supervisor] ${name} did not exit in ${Math.round(timeoutMs / 1000)}s, sending SIGKILL\n`
|
|
258
|
+
);
|
|
259
|
+
try {
|
|
260
|
+
child.kill('SIGKILL');
|
|
261
|
+
} catch {
|
|
262
|
+
// ignore
|
|
263
|
+
}
|
|
264
|
+
}, timeoutMs);
|
|
265
|
+
child.once('exit', () => {
|
|
266
|
+
clearTimeout(timer);
|
|
267
|
+
resolve();
|
|
268
|
+
});
|
|
269
|
+
try {
|
|
270
|
+
child.kill('SIGTERM');
|
|
271
|
+
} catch {
|
|
272
|
+
clearTimeout(timer);
|
|
273
|
+
resolve();
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { type FallbackSchema } from '../../shared/config.js';
|
|
2
2
|
import {
|
|
3
3
|
getActiveEnvironmentInfo,
|
|
4
|
-
|
|
4
|
+
getEnvironmentSearchDirs,
|
|
5
5
|
readEnvironment,
|
|
6
|
+
substituteLayeredEnvDir,
|
|
6
7
|
} from '../../shared/workspace.js';
|
|
7
8
|
import { z } from 'zod';
|
|
8
9
|
|
|
@@ -10,19 +11,21 @@ export type Fallback = z.infer<typeof FallbackSchema>;
|
|
|
10
11
|
|
|
11
12
|
function formatEnvironmentPrefix(
|
|
12
13
|
prefix: string,
|
|
13
|
-
|
|
14
|
+
searchDirs: string[],
|
|
15
|
+
replacements: { targetPath: string; executionCwd: string; envArgs: string }
|
|
14
16
|
): string {
|
|
17
|
+
let out = substituteLayeredEnvDir(prefix, searchDirs);
|
|
15
18
|
const map: Record<string, string> = {
|
|
16
19
|
'{WORKSPACE_DIR}': replacements.targetPath,
|
|
17
20
|
'{AGENT_DIR}': replacements.executionCwd,
|
|
18
|
-
'{ENV_DIR}': replacements.envDir,
|
|
19
21
|
'{HOME_DIR}': process.env.HOME || '',
|
|
20
22
|
'{ENV_ARGS}': replacements.envArgs,
|
|
21
23
|
};
|
|
22
|
-
|
|
23
|
-
/{(WORKSPACE_DIR|AGENT_DIR|
|
|
24
|
+
out = out.replace(
|
|
25
|
+
/{(WORKSPACE_DIR|AGENT_DIR|HOME_DIR|ENV_ARGS)}/g,
|
|
24
26
|
(match) => map[match] || match
|
|
25
27
|
);
|
|
28
|
+
return out;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export async function sandboxExecutionContext(
|
|
@@ -38,6 +41,7 @@ export async function sandboxExecutionContext(
|
|
|
38
41
|
|
|
39
42
|
const activeEnvName = activeEnvInfo.name;
|
|
40
43
|
const activeEnv = await readEnvironment(activeEnvName, cwd);
|
|
44
|
+
const searchDirs = await getEnvironmentSearchDirs(activeEnvName, cwd);
|
|
41
45
|
|
|
42
46
|
if (activeEnv?.env) {
|
|
43
47
|
for (const [key, value] of Object.entries(activeEnv.env)) {
|
|
@@ -47,10 +51,7 @@ export async function sandboxExecutionContext(
|
|
|
47
51
|
} else {
|
|
48
52
|
let interpolatedValue = String(value);
|
|
49
53
|
interpolatedValue = interpolatedValue.replace(/\{PATH\}/g, process.env.PATH || '');
|
|
50
|
-
interpolatedValue = interpolatedValue
|
|
51
|
-
/\{ENV_DIR\}/g,
|
|
52
|
-
getEnvironmentPath(activeEnvName, cwd)
|
|
53
|
-
);
|
|
54
|
+
interpolatedValue = substituteLayeredEnvDir(interpolatedValue, searchDirs);
|
|
54
55
|
interpolatedValue = interpolatedValue.replace(
|
|
55
56
|
/\{WORKSPACE_DIR\}/g,
|
|
56
57
|
activeEnvInfo.targetPath
|
|
@@ -71,10 +72,9 @@ export async function sandboxExecutionContext(
|
|
|
71
72
|
})
|
|
72
73
|
.join(' ');
|
|
73
74
|
|
|
74
|
-
const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, {
|
|
75
|
+
const prefixReplaced = formatEnvironmentPrefix(activeEnv.prefix, searchDirs, {
|
|
75
76
|
targetPath: activeEnvInfo.targetPath,
|
|
76
77
|
executionCwd: executionCwd,
|
|
77
|
-
envDir: getEnvironmentPath(activeEnvName, cwd),
|
|
78
78
|
envArgs,
|
|
79
79
|
});
|
|
80
80
|
|
|
@@ -23,6 +23,7 @@ export class AgentSession {
|
|
|
23
23
|
public readonly sessionId: string;
|
|
24
24
|
public readonly chatId: string;
|
|
25
25
|
public readonly subagentId: string | undefined;
|
|
26
|
+
public readonly turnId: string | undefined;
|
|
26
27
|
public readonly settings: Agent;
|
|
27
28
|
public readonly workspaceRoot: string;
|
|
28
29
|
public readonly globalSettings: Settings | undefined;
|
|
@@ -33,6 +34,7 @@ export class AgentSession {
|
|
|
33
34
|
sessionId: string;
|
|
34
35
|
chatId: string;
|
|
35
36
|
subagentId?: string;
|
|
37
|
+
turnId?: string;
|
|
36
38
|
settings: Agent;
|
|
37
39
|
workspaceRoot: string;
|
|
38
40
|
globalSettings: Settings | undefined;
|
|
@@ -42,11 +44,13 @@ export class AgentSession {
|
|
|
42
44
|
this.sessionId = config.sessionId;
|
|
43
45
|
this.chatId = config.chatId;
|
|
44
46
|
this.subagentId = config.subagentId;
|
|
47
|
+
this.turnId = config.turnId;
|
|
45
48
|
this.settings = config.settings;
|
|
46
49
|
this.workspaceRoot = config.workspaceRoot;
|
|
47
50
|
this.globalSettings = config.globalSettings;
|
|
48
51
|
|
|
49
|
-
this.logger =
|
|
52
|
+
this.logger =
|
|
53
|
+
config.logger ?? createChatLogger(this.chatId, this.subagentId, this.sessionId, this.turnId);
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
async buildExecutionContext(
|
|
@@ -110,6 +114,7 @@ export class AgentSession {
|
|
|
110
114
|
agentId: this.agentId,
|
|
111
115
|
sessionId: this.sessionId,
|
|
112
116
|
...(this.subagentId ? { subagentId: this.subagentId } : {}),
|
|
117
|
+
...(this.turnId ? { turnId: this.turnId } : {}),
|
|
113
118
|
timestamp: Date.now(),
|
|
114
119
|
});
|
|
115
120
|
|
|
@@ -215,6 +220,7 @@ export async function createAgentSession(options: {
|
|
|
215
220
|
agentId: string;
|
|
216
221
|
sessionId: string;
|
|
217
222
|
subagentId?: string;
|
|
223
|
+
turnId?: string;
|
|
218
224
|
cwd: string;
|
|
219
225
|
settings?: Settings | undefined;
|
|
220
226
|
logger?: Logger;
|
|
@@ -229,6 +235,7 @@ export async function createAgentSession(options: {
|
|
|
229
235
|
sessionId: options.sessionId,
|
|
230
236
|
chatId: options.chatId,
|
|
231
237
|
...(options.subagentId ? { subagentId: options.subagentId } : {}),
|
|
238
|
+
...(options.turnId ? { turnId: options.turnId } : {}),
|
|
232
239
|
settings: mergedAgent,
|
|
233
240
|
workspaceRoot,
|
|
234
241
|
globalSettings: settings,
|
|
@@ -38,11 +38,52 @@ describe('ChatLogger', () => {
|
|
|
38
38
|
);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
it('should inject sessionId into outgoing messages', async () => {
|
|
42
|
+
const logger = createChatLogger('chat-1', undefined, 'session-42');
|
|
43
|
+
await logger.logUserMessage('hello session');
|
|
44
|
+
await logger.logAgentReply({ content: 'reply' });
|
|
45
|
+
await logger.logCommandResult({
|
|
46
|
+
messageId: 'm',
|
|
47
|
+
content: '',
|
|
48
|
+
command: 'echo',
|
|
49
|
+
cwd: '/tmp',
|
|
50
|
+
result: { stdout: '', stderr: '', exitCode: 0 },
|
|
51
|
+
});
|
|
52
|
+
await logger.logToolMessage({
|
|
53
|
+
content: 'c',
|
|
54
|
+
messageId: 'm',
|
|
55
|
+
name: 't',
|
|
56
|
+
payload: {},
|
|
57
|
+
});
|
|
58
|
+
await logger.logSystemMessage({ content: 's', event: 'cron' });
|
|
59
|
+
|
|
60
|
+
const calls = vi.mocked(daemonChats.appendMessage).mock.calls;
|
|
61
|
+
expect(calls).toHaveLength(5);
|
|
62
|
+
for (const [, msg] of calls) {
|
|
63
|
+
expect(msg).toEqual(expect.objectContaining({ sessionId: 'session-42' }));
|
|
64
|
+
expect(msg).not.toHaveProperty('subagentId');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
41
68
|
it('should filter incoming logs for the subagent', async () => {
|
|
42
69
|
const mockMessages: ChatMessage[] = [
|
|
43
|
-
{ id: '1', role: 'user', content: 'root msg', timestamp: '1' },
|
|
44
|
-
{
|
|
45
|
-
|
|
70
|
+
{ id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
|
|
71
|
+
{
|
|
72
|
+
id: '2',
|
|
73
|
+
role: 'user',
|
|
74
|
+
content: 'sub msg',
|
|
75
|
+
timestamp: '2',
|
|
76
|
+
subagentId: 'sub-1',
|
|
77
|
+
sessionId: undefined,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: '3',
|
|
81
|
+
role: 'user',
|
|
82
|
+
content: 'other sub',
|
|
83
|
+
timestamp: '3',
|
|
84
|
+
subagentId: 'sub-2',
|
|
85
|
+
sessionId: undefined,
|
|
86
|
+
},
|
|
46
87
|
];
|
|
47
88
|
vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
|
|
48
89
|
|
|
@@ -56,10 +97,31 @@ describe('ChatLogger', () => {
|
|
|
56
97
|
|
|
57
98
|
it('should limit messages after filtering', async () => {
|
|
58
99
|
const mockMessages: ChatMessage[] = [
|
|
59
|
-
{ id: '1', role: 'user', content: 'root msg', timestamp: '1' },
|
|
60
|
-
{
|
|
61
|
-
|
|
62
|
-
|
|
100
|
+
{ id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
|
|
101
|
+
{
|
|
102
|
+
id: '2',
|
|
103
|
+
role: 'user',
|
|
104
|
+
content: 'sub msg 1',
|
|
105
|
+
timestamp: '2',
|
|
106
|
+
subagentId: 'sub-1',
|
|
107
|
+
sessionId: undefined,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: '3',
|
|
111
|
+
role: 'user',
|
|
112
|
+
content: 'other sub',
|
|
113
|
+
timestamp: '3',
|
|
114
|
+
subagentId: 'sub-2',
|
|
115
|
+
sessionId: undefined,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: '4',
|
|
119
|
+
role: 'user',
|
|
120
|
+
content: 'sub msg 2',
|
|
121
|
+
timestamp: '4',
|
|
122
|
+
subagentId: 'sub-1',
|
|
123
|
+
sessionId: undefined,
|
|
124
|
+
},
|
|
63
125
|
];
|
|
64
126
|
vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
|
|
65
127
|
|
|
@@ -72,8 +134,15 @@ describe('ChatLogger', () => {
|
|
|
72
134
|
|
|
73
135
|
it('should not return subagent messages if no subagentId', async () => {
|
|
74
136
|
const mockMessages: ChatMessage[] = [
|
|
75
|
-
{ id: '1', role: 'user', content: 'root msg', timestamp: '1' },
|
|
76
|
-
{
|
|
137
|
+
{ id: '1', role: 'user', content: 'root msg', timestamp: '1', sessionId: undefined },
|
|
138
|
+
{
|
|
139
|
+
id: '2',
|
|
140
|
+
role: 'user',
|
|
141
|
+
content: 'sub msg 1',
|
|
142
|
+
timestamp: '2',
|
|
143
|
+
subagentId: 'sub-1',
|
|
144
|
+
sessionId: undefined,
|
|
145
|
+
},
|
|
77
146
|
];
|
|
78
147
|
vi.mocked(daemonChats.getMessages).mockResolvedValue(mockMessages);
|
|
79
148
|
|
|
@@ -13,11 +13,18 @@ import {
|
|
|
13
13
|
} from '../chats.js';
|
|
14
14
|
import type { Logger } from './types.js';
|
|
15
15
|
|
|
16
|
-
export function createChatLogger(
|
|
16
|
+
export function createChatLogger(
|
|
17
|
+
chatId: string,
|
|
18
|
+
subagentId?: string,
|
|
19
|
+
sessionId?: string,
|
|
20
|
+
turnId?: string
|
|
21
|
+
): Logger {
|
|
17
22
|
async function append<T extends ChatMessage>(msg: T): Promise<T> {
|
|
18
|
-
|
|
23
|
+
let finalMsg: T = msg;
|
|
24
|
+
if (subagentId) finalMsg = { ...finalMsg, subagentId };
|
|
25
|
+
if (turnId) finalMsg = { ...finalMsg, turnId };
|
|
19
26
|
await appendMessage(chatId, finalMsg);
|
|
20
|
-
return finalMsg
|
|
27
|
+
return finalMsg;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
return {
|
|
@@ -45,6 +52,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
45
52
|
role: 'user',
|
|
46
53
|
content: msg,
|
|
47
54
|
timestamp: new Date().toISOString(),
|
|
55
|
+
sessionId,
|
|
48
56
|
} satisfies UserMessage),
|
|
49
57
|
|
|
50
58
|
logCommandResult: async ({ messageId, content, command, cwd, result }) =>
|
|
@@ -53,6 +61,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
53
61
|
role: 'command',
|
|
54
62
|
content,
|
|
55
63
|
timestamp: new Date().toISOString(),
|
|
64
|
+
sessionId,
|
|
56
65
|
|
|
57
66
|
messageId,
|
|
58
67
|
|
|
@@ -61,7 +70,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
61
70
|
stdout: result.stdout,
|
|
62
71
|
stderr: result.stderr,
|
|
63
72
|
exitCode: result.exitCode,
|
|
64
|
-
}),
|
|
73
|
+
} satisfies CommandLogMessage),
|
|
65
74
|
|
|
66
75
|
logSystemEvent: async ({ content }) =>
|
|
67
76
|
append({
|
|
@@ -69,6 +78,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
69
78
|
role: 'command',
|
|
70
79
|
content,
|
|
71
80
|
timestamp: new Date().toISOString(),
|
|
81
|
+
sessionId,
|
|
72
82
|
|
|
73
83
|
messageId: crypto.randomUUID(),
|
|
74
84
|
|
|
@@ -85,6 +95,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
85
95
|
role: 'system',
|
|
86
96
|
content,
|
|
87
97
|
timestamp: new Date().toISOString(),
|
|
98
|
+
sessionId,
|
|
88
99
|
|
|
89
100
|
messageId,
|
|
90
101
|
event: 'router',
|
|
@@ -97,6 +108,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
97
108
|
role: 'command',
|
|
98
109
|
content,
|
|
99
110
|
timestamp: new Date().toISOString(),
|
|
111
|
+
sessionId,
|
|
100
112
|
|
|
101
113
|
messageId,
|
|
102
114
|
|
|
@@ -108,13 +120,14 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
108
120
|
exitCode: 0,
|
|
109
121
|
} satisfies CommandLogMessage),
|
|
110
122
|
|
|
111
|
-
logSystemMessage: async ({ content, event, messageId, displayRole }) => {
|
|
123
|
+
logSystemMessage: async ({ content, event, messageId, displayRole, jobId }) => {
|
|
112
124
|
const msg: SystemMessage = {
|
|
113
125
|
id: crypto.randomUUID(),
|
|
114
126
|
role: 'system',
|
|
115
127
|
content,
|
|
116
128
|
event,
|
|
117
129
|
timestamp: new Date().toISOString(),
|
|
130
|
+
sessionId,
|
|
118
131
|
};
|
|
119
132
|
if (messageId !== undefined) {
|
|
120
133
|
msg.messageId = messageId;
|
|
@@ -122,6 +135,9 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
122
135
|
if (displayRole !== undefined) {
|
|
123
136
|
msg.displayRole = displayRole;
|
|
124
137
|
}
|
|
138
|
+
if (jobId !== undefined) {
|
|
139
|
+
msg.jobId = jobId;
|
|
140
|
+
}
|
|
125
141
|
return append<SystemMessage>(msg);
|
|
126
142
|
},
|
|
127
143
|
|
|
@@ -133,6 +149,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
133
149
|
subagentId: targetSubagentId,
|
|
134
150
|
status,
|
|
135
151
|
timestamp: new Date().toISOString(),
|
|
152
|
+
sessionId,
|
|
136
153
|
};
|
|
137
154
|
return append<SubagentStatusMessage>(msg);
|
|
138
155
|
},
|
|
@@ -143,6 +160,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
143
160
|
role: 'agent',
|
|
144
161
|
content,
|
|
145
162
|
timestamp: new Date().toISOString(),
|
|
163
|
+
sessionId,
|
|
146
164
|
};
|
|
147
165
|
if (files !== undefined) {
|
|
148
166
|
msg.files = files;
|
|
@@ -159,6 +177,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
159
177
|
name,
|
|
160
178
|
payload,
|
|
161
179
|
timestamp: new Date().toISOString(),
|
|
180
|
+
sessionId,
|
|
162
181
|
};
|
|
163
182
|
return append<ToolMessage>(msg);
|
|
164
183
|
},
|
|
@@ -181,6 +200,7 @@ export function createChatLogger(chatId: string, subagentId?: string): Logger {
|
|
|
181
200
|
args,
|
|
182
201
|
status,
|
|
183
202
|
timestamp: new Date().toISOString(),
|
|
203
|
+
sessionId,
|
|
184
204
|
};
|
|
185
205
|
return append<PolicyRequestMessage>(msg);
|
|
186
206
|
},
|