clawmini 0.0.8 → 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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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 +0 -1
- 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 -118
- package/web/.svelte-kit/generated/server/internal.js +1 -1
- package/web/.svelte-kit/output/client/.vite/manifest.json +126 -136
- 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/{vDehDcuJ.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.CUGC2p-K.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.0arZe_Uf.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.Bq2JzCEj.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/D5iV40bG.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/entry/app.BCSV3nrG.js +0 -2
- package/dist/web/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/dist/web/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/dist/web/_app/immutable/nodes/4.ClM1bXLE.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/D5iV40bG.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/entry/app.BCSV3nrG.js +0 -2
- package/web/.svelte-kit/output/client/_app/immutable/entry/start.D4eLEZUM.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/1.CGC_42IQ.js +0 -1
- package/web/.svelte-kit/output/client/_app/immutable/nodes/4.ClM1bXLE.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,357 @@
|
|
|
1
|
+
import type { ChatMessage } from '../chats.js';
|
|
2
|
+
|
|
3
|
+
export interface TurnLogEntry {
|
|
4
|
+
timestamp: string;
|
|
5
|
+
kind: 'tool' | 'subagent' | 'policy' | 'system';
|
|
6
|
+
summary: string;
|
|
7
|
+
rawLength: number;
|
|
8
|
+
subagentId?: string;
|
|
9
|
+
messageRole: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface FormatOpts {
|
|
13
|
+
maxToolPreview?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Reference time for rendering relative timestamps (e.g. `0s`, `1m5s`).
|
|
16
|
+
* When omitted, timestamps fall back to wall-clock `HH:MM:SS`.
|
|
17
|
+
*/
|
|
18
|
+
turnStartedAt?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CondenseOpts {
|
|
22
|
+
maxChars: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type CondenseResult =
|
|
26
|
+
| { kind: 'fits'; text: string }
|
|
27
|
+
| { kind: 'rollover'; finalText: string; carryEntries: TurnLogEntry[] };
|
|
28
|
+
|
|
29
|
+
const DEFAULT_MAX_TOOL_PREVIEW = 400;
|
|
30
|
+
const TRUNCATED_SUFFIX = '…[truncated]';
|
|
31
|
+
const ROLLOVER_MARKER = '• …log continues';
|
|
32
|
+
|
|
33
|
+
function pad2(n: number): string {
|
|
34
|
+
return n < 10 ? `0${n}` : String(n);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatRelative(deltaMs: number): string {
|
|
38
|
+
const sec = Math.max(0, Math.floor(deltaMs / 1000));
|
|
39
|
+
if (sec < 60) return `${sec}s`;
|
|
40
|
+
const m = Math.floor(sec / 60);
|
|
41
|
+
const s = sec % 60;
|
|
42
|
+
return s === 0 ? `${m}m` : `${m}m${s}s`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function formatTimestamp(iso: string, turnStartedAt?: string): string {
|
|
46
|
+
if (turnStartedAt) {
|
|
47
|
+
const start = new Date(turnStartedAt).getTime();
|
|
48
|
+
const now = iso ? new Date(iso).getTime() : Date.now();
|
|
49
|
+
if (!Number.isNaN(start) && !Number.isNaN(now)) {
|
|
50
|
+
return formatRelative(now - start);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const d = iso ? new Date(iso) : new Date();
|
|
54
|
+
const valid = !Number.isNaN(d.getTime()) ? d : new Date();
|
|
55
|
+
return `${pad2(valid.getHours())}:${pad2(valid.getMinutes())}:${pad2(valid.getSeconds())}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
59
|
+
|
|
60
|
+
function shortSubagentId(id: string): string {
|
|
61
|
+
// UUIDs are visual noise; the first hex segment is enough to disambiguate.
|
|
62
|
+
// User-supplied ids (e.g. `hello-sub`) are typically short already.
|
|
63
|
+
return UUID_RE.test(id) ? id.slice(0, 8) : id;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ToolPrincipal {
|
|
67
|
+
verb: string;
|
|
68
|
+
arg: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Extract a (verb, principal-arg) pair from a tool message so the turn log
|
|
73
|
+
* can render `read: foo.md` instead of `read_file({ "file_path": "foo.md" })`.
|
|
74
|
+
* Unknown tool names fall back to `<name>: <stringified-payload>` so we don't
|
|
75
|
+
* silently lose information.
|
|
76
|
+
*/
|
|
77
|
+
function extractToolPrincipal(name: string, payload: unknown): ToolPrincipal {
|
|
78
|
+
const p = (payload ?? {}) as Record<string, unknown>;
|
|
79
|
+
const str = (v: unknown) => (v == null ? '' : String(v));
|
|
80
|
+
|
|
81
|
+
switch (name) {
|
|
82
|
+
case 'read_file':
|
|
83
|
+
case 'Read':
|
|
84
|
+
return { verb: 'read', arg: str(p.file_path ?? p.path) };
|
|
85
|
+
case 'write_file':
|
|
86
|
+
case 'create_file':
|
|
87
|
+
case 'Write':
|
|
88
|
+
return { verb: 'write', arg: str(p.file_path ?? p.path) };
|
|
89
|
+
case 'edit_file':
|
|
90
|
+
case 'Edit':
|
|
91
|
+
return { verb: 'edit', arg: str(p.file_path ?? p.path) };
|
|
92
|
+
case 'run_shell_command':
|
|
93
|
+
case 'shell':
|
|
94
|
+
case 'Bash':
|
|
95
|
+
return { verb: 'shell', arg: str(p.command) };
|
|
96
|
+
case 'activate_skill':
|
|
97
|
+
case 'Skill':
|
|
98
|
+
return { verb: 'skill', arg: str(p.name ?? p.skill) };
|
|
99
|
+
case 'glob':
|
|
100
|
+
case 'Glob':
|
|
101
|
+
return { verb: 'glob', arg: str(p.pattern) };
|
|
102
|
+
case 'grep':
|
|
103
|
+
case 'Grep':
|
|
104
|
+
return { verb: 'grep', arg: str(p.pattern) };
|
|
105
|
+
case 'web_fetch':
|
|
106
|
+
case 'WebFetch':
|
|
107
|
+
return { verb: 'fetch', arg: str(p.url) };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const arg = payload === undefined || payload === null ? '' : JSON.stringify(payload);
|
|
111
|
+
return { verb: name, arg };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function statusSigil(status: 'completed' | 'failed'): string {
|
|
115
|
+
return status === 'completed' ? '✅' : '❌';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Emoji for messages crossing the subagent boundary. */
|
|
119
|
+
const SUBAGENT_TO = '👉';
|
|
120
|
+
const SUBAGENT_FROM = '👈';
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Emoji that stands in for a known tool verb. When present, it replaces the
|
|
124
|
+
* verb word entirely (`🐚 sleep 20` instead of `shell: sleep 20`). Unknown
|
|
125
|
+
* tools fall through to the `<name>: <arg>` form, preserving accessibility.
|
|
126
|
+
*/
|
|
127
|
+
const VERB_EMOJI: Record<string, string> = {
|
|
128
|
+
read: '📖',
|
|
129
|
+
write: '✍️',
|
|
130
|
+
edit: '✏️',
|
|
131
|
+
shell: '🧑💻',
|
|
132
|
+
skill: '📚',
|
|
133
|
+
glob: '📁',
|
|
134
|
+
grep: '🔎',
|
|
135
|
+
fetch: '🌐',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const SUBAGENT_MARKER = '🤖';
|
|
139
|
+
|
|
140
|
+
/** Emoji rendered on the turn's opening entry (posted when the turn starts). */
|
|
141
|
+
export const TURN_START_EMOJI = '▶️';
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Entries produced *inside* a subagent (a tool call, policy, system event
|
|
145
|
+
* with `subagentId` set) need a marker so the reader knows the activity
|
|
146
|
+
* happened inside the delegated turn. Boundary events (prompt, reply, status)
|
|
147
|
+
* already name the subagent via 👉/👈/✅, so they're excluded.
|
|
148
|
+
*/
|
|
149
|
+
function needsSubagentMarker(entry: TurnLogEntry): boolean {
|
|
150
|
+
if (!entry.subagentId) return false;
|
|
151
|
+
return (
|
|
152
|
+
entry.messageRole !== 'user' &&
|
|
153
|
+
entry.messageRole !== 'agent' &&
|
|
154
|
+
entry.messageRole !== 'subagent_status'
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function truncate(s: string, max: number): string {
|
|
159
|
+
if (s.length <= max) return s;
|
|
160
|
+
const budget = Math.max(0, max - TRUNCATED_SUFFIX.length);
|
|
161
|
+
return s.slice(0, budget) + TRUNCATED_SUFFIX;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function sanitize(s: string): string {
|
|
165
|
+
return s.replace(/\s*\r?\n\s*/g, ' ').trim();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderEntry(entry: TurnLogEntry): string {
|
|
169
|
+
const prefix = needsSubagentMarker(entry)
|
|
170
|
+
? `${SUBAGENT_MARKER} ${shortSubagentId(entry.subagentId!)} `
|
|
171
|
+
: '';
|
|
172
|
+
return `• ${entry.timestamp} ${prefix}${entry.summary}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Build the first entry posted into a turn's activity log so the thread
|
|
177
|
+
* appears as soon as `turnStarted` fires, rather than waiting for the first
|
|
178
|
+
* real event.
|
|
179
|
+
*/
|
|
180
|
+
export function buildTurnStartEntry(): TurnLogEntry {
|
|
181
|
+
const summary = `${TURN_START_EMOJI} Started processing…`;
|
|
182
|
+
return {
|
|
183
|
+
timestamp: '0s',
|
|
184
|
+
kind: 'system',
|
|
185
|
+
summary,
|
|
186
|
+
rawLength: summary.length,
|
|
187
|
+
messageRole: 'turn_start',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function formatTurnLogEntry(
|
|
192
|
+
message: ChatMessage,
|
|
193
|
+
opts: FormatOpts = {}
|
|
194
|
+
): TurnLogEntry | null {
|
|
195
|
+
const maxToolPreview = opts.maxToolPreview ?? DEFAULT_MAX_TOOL_PREVIEW;
|
|
196
|
+
const timestamp = formatTimestamp(message.timestamp, opts.turnStartedAt);
|
|
197
|
+
|
|
198
|
+
if (message.role === 'user' || message.role === 'agent') {
|
|
199
|
+
if (!message.subagentId) return null;
|
|
200
|
+
// Subagent prompts and final replies are part of the parent turn's
|
|
201
|
+
// activity: the prompt shows what the subagent was told to do, the reply
|
|
202
|
+
// shows what it produced. Render them in the log so the reader can follow
|
|
203
|
+
// the orchestration without switching context.
|
|
204
|
+
const direction = message.role === 'user' ? SUBAGENT_TO : SUBAGENT_FROM;
|
|
205
|
+
const content = sanitize(message.content);
|
|
206
|
+
const id = shortSubagentId(message.subagentId);
|
|
207
|
+
const summary = `${direction} ${id}: ${truncate(content, maxToolPreview)}`;
|
|
208
|
+
return {
|
|
209
|
+
timestamp,
|
|
210
|
+
kind: 'subagent',
|
|
211
|
+
summary,
|
|
212
|
+
rawLength: content.length,
|
|
213
|
+
messageRole: message.role,
|
|
214
|
+
subagentId: message.subagentId,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (message.role === 'tool') {
|
|
219
|
+
const { verb, arg } = extractToolPrincipal(message.name, message.payload);
|
|
220
|
+
const cleanArg = sanitize(arg);
|
|
221
|
+
const emoji = VERB_EMOJI[verb];
|
|
222
|
+
const argPreview = cleanArg ? truncate(cleanArg, maxToolPreview) : '';
|
|
223
|
+
let summary: string;
|
|
224
|
+
if (emoji) {
|
|
225
|
+
summary = argPreview ? `${emoji} ${argPreview}` : emoji;
|
|
226
|
+
} else {
|
|
227
|
+
summary = argPreview ? `${verb}: ${argPreview}` : verb;
|
|
228
|
+
}
|
|
229
|
+
const entry: TurnLogEntry = {
|
|
230
|
+
timestamp,
|
|
231
|
+
kind: 'tool',
|
|
232
|
+
summary,
|
|
233
|
+
rawLength: cleanArg.length,
|
|
234
|
+
messageRole: message.role,
|
|
235
|
+
};
|
|
236
|
+
if (message.subagentId) entry.subagentId = message.subagentId;
|
|
237
|
+
return entry;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (message.role === 'subagent_status') {
|
|
241
|
+
const id = shortSubagentId(message.subagentId);
|
|
242
|
+
const summary = `${statusSigil(message.status)} ${id}`;
|
|
243
|
+
const entry: TurnLogEntry = {
|
|
244
|
+
timestamp,
|
|
245
|
+
kind: 'subagent',
|
|
246
|
+
summary,
|
|
247
|
+
rawLength: summary.length,
|
|
248
|
+
messageRole: message.role,
|
|
249
|
+
subagentId: message.subagentId,
|
|
250
|
+
};
|
|
251
|
+
return entry;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (message.role === 'policy') {
|
|
255
|
+
const body = `${message.commandName} ${message.args.join(' ')}`.trim();
|
|
256
|
+
const summary = `policy ${message.status}: ${body}`;
|
|
257
|
+
const entry: TurnLogEntry = {
|
|
258
|
+
timestamp,
|
|
259
|
+
kind: 'policy',
|
|
260
|
+
summary: truncate(sanitize(summary), maxToolPreview),
|
|
261
|
+
rawLength: summary.length,
|
|
262
|
+
messageRole: message.role,
|
|
263
|
+
};
|
|
264
|
+
if (message.subagentId) entry.subagentId = message.subagentId;
|
|
265
|
+
return entry;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (message.role === 'system') {
|
|
269
|
+
// subagent_update is the wake-up signal that re-enters the parent agent
|
|
270
|
+
// after an async subagent completes. Its content is a `<notification>`
|
|
271
|
+
// envelope — internal orchestration the reader doesn't need. The ✅ from
|
|
272
|
+
// subagent_status already conveys completion, and the parent's follow-up
|
|
273
|
+
// reply shows the response.
|
|
274
|
+
if (message.event === 'subagent_update') return null;
|
|
275
|
+
const content = sanitize(message.content || '');
|
|
276
|
+
const summary = content ? `${message.event}: ${content}` : message.event;
|
|
277
|
+
const entry: TurnLogEntry = {
|
|
278
|
+
timestamp,
|
|
279
|
+
kind: 'system',
|
|
280
|
+
summary: truncate(summary, maxToolPreview),
|
|
281
|
+
rawLength: summary.length,
|
|
282
|
+
messageRole: message.role,
|
|
283
|
+
};
|
|
284
|
+
if (message.subagentId) entry.subagentId = message.subagentId;
|
|
285
|
+
return entry;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (message.role === 'command') {
|
|
289
|
+
// The raw shell command (usually the agent template wrapping
|
|
290
|
+
// $CLAW_CLI_MESSAGE) isn't informative to a reader skimming the log;
|
|
291
|
+
// tool calls and subagent events already describe what the agent did.
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (message.role === 'legacy_log') {
|
|
296
|
+
const content = sanitize(message.content);
|
|
297
|
+
const summary = content ? `log: ${content}` : 'log';
|
|
298
|
+
const entry: TurnLogEntry = {
|
|
299
|
+
timestamp,
|
|
300
|
+
kind: 'system',
|
|
301
|
+
summary: truncate(summary, maxToolPreview),
|
|
302
|
+
rawLength: content.length,
|
|
303
|
+
messageRole: message.role,
|
|
304
|
+
};
|
|
305
|
+
if (message.subagentId) entry.subagentId = message.subagentId;
|
|
306
|
+
return entry;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function joinLines(entries: TurnLogEntry[]): string {
|
|
313
|
+
return entries.map(renderEntry).join('\n');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function condenseTurnLog(
|
|
317
|
+
entries: readonly TurnLogEntry[],
|
|
318
|
+
opts: CondenseOpts
|
|
319
|
+
): CondenseResult {
|
|
320
|
+
const snapshot = entries.slice();
|
|
321
|
+
if (snapshot.length === 0) return { kind: 'fits', text: '' };
|
|
322
|
+
|
|
323
|
+
const fullText = joinLines(snapshot);
|
|
324
|
+
if (fullText.length <= opts.maxChars) return { kind: 'fits', text: fullText };
|
|
325
|
+
|
|
326
|
+
const markerReserve = ROLLOVER_MARKER.length + 1;
|
|
327
|
+
const budget = Math.max(0, opts.maxChars - markerReserve);
|
|
328
|
+
|
|
329
|
+
const kept: TurnLogEntry[] = [];
|
|
330
|
+
let runningLength = 0;
|
|
331
|
+
for (let i = 0; i < snapshot.length; i++) {
|
|
332
|
+
const line = renderEntry(snapshot[i]!);
|
|
333
|
+
const next = runningLength === 0 ? line.length : runningLength + 1 + line.length;
|
|
334
|
+
if (next > budget) {
|
|
335
|
+
if (kept.length === 0) {
|
|
336
|
+
// Single entry's line is larger than the per-message budget: hard-
|
|
337
|
+
// truncate its rendered form so we don't get stuck in a "same carry"
|
|
338
|
+
// rollover loop. The continuation marker is baked into the truncated
|
|
339
|
+
// text rather than appended, so the reader still sees the `…` signal.
|
|
340
|
+
const truncatedLine = truncate(line, opts.maxChars);
|
|
341
|
+
return {
|
|
342
|
+
kind: 'rollover',
|
|
343
|
+
finalText: truncatedLine,
|
|
344
|
+
carryEntries: snapshot.slice(i + 1),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
const carryEntries = snapshot.slice(i);
|
|
348
|
+
const finalText = `${joinLines(kept)}\n${ROLLOVER_MARKER}`;
|
|
349
|
+
return { kind: 'rollover', finalText, carryEntries };
|
|
350
|
+
}
|
|
351
|
+
runningLength = next;
|
|
352
|
+
kept.push(snapshot[i]!);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Should not reach here; fallback.
|
|
356
|
+
return { kind: 'fits', text: fullText };
|
|
357
|
+
}
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
applyTemplateToAgent,
|
|
5
5
|
readChatSettings,
|
|
6
6
|
writeChatSettings,
|
|
7
|
-
|
|
7
|
+
refreshAgentSkills,
|
|
8
|
+
getAgent,
|
|
8
9
|
} from './workspace.js';
|
|
9
10
|
import { createChat, listChats } from './chats.js';
|
|
10
11
|
|
|
@@ -12,17 +13,23 @@ export async function createAgentWithChat(
|
|
|
12
13
|
agentId: string,
|
|
13
14
|
agentData: Agent,
|
|
14
15
|
template?: string,
|
|
15
|
-
startDir = process.cwd()
|
|
16
|
+
startDir = process.cwd(),
|
|
17
|
+
opts: { fork?: boolean; force?: boolean } = {}
|
|
16
18
|
): Promise<void> {
|
|
17
19
|
await writeAgentSettings(agentId, agentData, startDir);
|
|
18
20
|
|
|
19
21
|
if (template) {
|
|
20
|
-
await applyTemplateToAgent(agentId, template, agentData, startDir);
|
|
22
|
+
await applyTemplateToAgent(agentId, template, agentData, startDir, opts);
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
try {
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
+
const resolved = await getAgent(agentId, startDir);
|
|
27
|
+
if (resolved?.skillsDir === null) {
|
|
28
|
+
console.log(`Skipping skills for agent ${agentId} (skillsDir is null).`);
|
|
29
|
+
} else if (resolved) {
|
|
30
|
+
await refreshAgentSkills(agentId, resolved, startDir, { firstInstall: true });
|
|
31
|
+
console.log(`Installed skills for agent ${agentId}.`);
|
|
32
|
+
}
|
|
26
33
|
} catch (err) {
|
|
27
34
|
console.warn(
|
|
28
35
|
`Warning: Failed to copy skills to agent ${agentId}: ${err instanceof Error ? err.message : String(err)}`
|
package/src/shared/chats.test.ts
CHANGED
|
@@ -58,6 +58,7 @@ describe('chats utilities', () => {
|
|
|
58
58
|
role: 'user',
|
|
59
59
|
content: 'Hello',
|
|
60
60
|
timestamp: new Date().toISOString(),
|
|
61
|
+
sessionId: undefined,
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
const msg2: CommandLogMessage = {
|
|
@@ -71,6 +72,7 @@ describe('chats utilities', () => {
|
|
|
71
72
|
command: 'echo output',
|
|
72
73
|
cwd: '/tmp',
|
|
73
74
|
exitCode: 0,
|
|
75
|
+
sessionId: undefined,
|
|
74
76
|
};
|
|
75
77
|
|
|
76
78
|
const msg3: CommandLogMessage = {
|
|
@@ -84,6 +86,7 @@ describe('chats utilities', () => {
|
|
|
84
86
|
command: 'router',
|
|
85
87
|
cwd: '/tmp',
|
|
86
88
|
exitCode: 0,
|
|
89
|
+
sessionId: undefined,
|
|
87
90
|
};
|
|
88
91
|
|
|
89
92
|
await appendMessage('chat1', msg1, TEST_DIR);
|
|
@@ -129,6 +132,7 @@ describe('chats utilities', () => {
|
|
|
129
132
|
role: 'user',
|
|
130
133
|
content: `Message ${i + 1}`,
|
|
131
134
|
timestamp: new Date().toISOString(),
|
|
135
|
+
sessionId: undefined,
|
|
132
136
|
}));
|
|
133
137
|
|
|
134
138
|
for (const msg of msgs) {
|
package/src/shared/chats.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface BaseMessage {
|
|
|
14
14
|
content: string;
|
|
15
15
|
timestamp: string;
|
|
16
16
|
subagentId?: string;
|
|
17
|
+
sessionId: string | undefined;
|
|
18
|
+
turnId?: string;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface UserMessage extends BaseMessage {
|
|
@@ -48,6 +50,12 @@ export interface SystemMessage extends BaseMessage {
|
|
|
48
50
|
role: 'system';
|
|
49
51
|
event: 'cron' | 'policy_approved' | 'policy_rejected' | 'subagent_update' | 'router' | 'other';
|
|
50
52
|
messageId?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Populated on `event === 'cron'` with the CronJob id that fired. Used by
|
|
55
|
+
* adapters (gchat `visibility.jobs: 'header'`) to render a terse header
|
|
56
|
+
* instead of the agent-facing prompt.
|
|
57
|
+
*/
|
|
58
|
+
jobId?: string;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
export interface ToolMessage extends BaseMessage {
|
|
@@ -70,6 +78,7 @@ export interface SubagentStatusMessage extends BaseMessage {
|
|
|
70
78
|
role: 'subagent_status';
|
|
71
79
|
subagentId: string;
|
|
72
80
|
status: 'completed' | 'failed';
|
|
81
|
+
turnId?: string;
|
|
73
82
|
}
|
|
74
83
|
|
|
75
84
|
export interface LegacyLogMessage extends BaseMessage {
|
package/src/shared/config.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
const PolicyDefinitionSchema = z.looseObject({
|
|
4
|
+
description: z.string().optional(),
|
|
5
|
+
command: z.string(),
|
|
6
|
+
args: z.array(z.string()).optional(),
|
|
7
|
+
allowHelp: z.boolean().optional(),
|
|
8
|
+
autoApprove: z.boolean().optional(),
|
|
9
|
+
});
|
|
10
|
+
|
|
3
11
|
export const FallbackSchema = z.looseObject({
|
|
4
12
|
commands: z
|
|
5
13
|
.looseObject({
|
|
@@ -15,6 +23,7 @@ export const FallbackSchema = z.looseObject({
|
|
|
15
23
|
});
|
|
16
24
|
|
|
17
25
|
export const AgentSchema = z.looseObject({
|
|
26
|
+
extends: z.string().optional(),
|
|
18
27
|
commands: z
|
|
19
28
|
.looseObject({
|
|
20
29
|
new: z.string().optional(),
|
|
@@ -27,8 +36,11 @@ export const AgentSchema = z.looseObject({
|
|
|
27
36
|
apiUrlEnvVar: z.string().optional(),
|
|
28
37
|
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
29
38
|
subagentEnv: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
39
|
+
modelShorthands: z.record(z.string(), z.string()).optional(),
|
|
30
40
|
directory: z.string().optional(),
|
|
31
|
-
|
|
41
|
+
// `null` explicitly disables skill install/refresh for this agent.
|
|
42
|
+
// `undefined` (omitted) falls back to the template's value or `.agents/skills`.
|
|
43
|
+
skillsDir: z.string().nullable().optional(),
|
|
32
44
|
fallbacks: z.array(FallbackSchema).optional(),
|
|
33
45
|
files: z.string().default('./attachments').optional(),
|
|
34
46
|
});
|
|
@@ -116,13 +128,16 @@ export const AgentSessionSettingsSchema = z.looseObject({
|
|
|
116
128
|
export type AgentSessionSettings = z.infer<typeof AgentSessionSettingsSchema>;
|
|
117
129
|
|
|
118
130
|
export const EnvironmentSchema = z.looseObject({
|
|
131
|
+
extends: z.string().optional(),
|
|
119
132
|
init: z.string().optional(),
|
|
120
133
|
up: z.string().optional(),
|
|
121
134
|
down: z.string().optional(),
|
|
122
135
|
prefix: z.string().optional(),
|
|
123
136
|
envFormat: z.string().optional(),
|
|
124
137
|
exportLiteTo: z.string().optional(),
|
|
138
|
+
baseDir: z.string().optional(),
|
|
125
139
|
env: z.record(z.string(), z.union([z.string(), z.boolean()])).optional(),
|
|
140
|
+
policies: z.record(z.string(), PolicyDefinitionSchema).optional(),
|
|
126
141
|
});
|
|
127
142
|
|
|
128
143
|
export type Environment = z.infer<typeof EnvironmentSchema>;
|
package/src/shared/lite.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { readSettings, readEnvironment, getWorkspaceRoot } from './workspace.js';
|
|
5
6
|
import type { Environment } from './config.js';
|
|
6
7
|
|
|
8
|
+
const LITE_MARKER = 'clawmini-lite';
|
|
9
|
+
|
|
10
|
+
function sha256(content: string): string {
|
|
11
|
+
return createHash('sha256').update(content).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function looksLikeLiteScript(content: string): boolean {
|
|
15
|
+
return content.startsWith('#!') && content.includes(LITE_MARKER);
|
|
16
|
+
}
|
|
17
|
+
|
|
7
18
|
export async function resolveCompiledScript(scriptName: string, metaUrl: string): Promise<string> {
|
|
8
19
|
const __dirname = path.dirname(fileURLToPath(metaUrl));
|
|
9
20
|
const filename = scriptName.endsWith('.mjs') ? scriptName : `${scriptName}.mjs`;
|
|
@@ -77,6 +88,63 @@ export async function writeLiteScript(outPath: string): Promise<string> {
|
|
|
77
88
|
return finalPath;
|
|
78
89
|
}
|
|
79
90
|
|
|
91
|
+
async function resolveLiteTargetPath(outPath: string): Promise<string> {
|
|
92
|
+
const isProbablyFile =
|
|
93
|
+
path.extname(outPath) === '.js' ||
|
|
94
|
+
path.extname(outPath) === '.mjs' ||
|
|
95
|
+
path.basename(outPath) === 'clawmini-lite';
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const stat = await fs.stat(outPath);
|
|
99
|
+
if (stat.isDirectory()) {
|
|
100
|
+
return path.join(outPath, 'clawmini-lite.js');
|
|
101
|
+
}
|
|
102
|
+
return outPath;
|
|
103
|
+
} catch {
|
|
104
|
+
if (isProbablyFile) return outPath;
|
|
105
|
+
// No extension and path doesn't yet exist — treat as a directory target.
|
|
106
|
+
return path.join(outPath, 'clawmini-lite.js');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Content-hashed write that refuses to clobber an arbitrary user file at the
|
|
111
|
+
// export path. Returns:
|
|
112
|
+
// - 'written' when the file was created or updated
|
|
113
|
+
// - 'unchanged' when the on-disk content already matches (no mtime touch)
|
|
114
|
+
// - 'refused' when the existing file looks like it isn't a clawmini-lite
|
|
115
|
+
export async function refreshLiteAt(
|
|
116
|
+
outPath: string,
|
|
117
|
+
opts: { label?: string } = {}
|
|
118
|
+
): Promise<{ status: 'written' | 'unchanged' | 'refused'; path: string }> {
|
|
119
|
+
const content = await getLiteScriptContent();
|
|
120
|
+
const finalPath = await resolveLiteTargetPath(outPath);
|
|
121
|
+
|
|
122
|
+
let existing: string | null = null;
|
|
123
|
+
try {
|
|
124
|
+
existing = await fs.readFile(finalPath, 'utf8');
|
|
125
|
+
} catch {
|
|
126
|
+
// missing — write below
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (existing !== null) {
|
|
130
|
+
if (sha256(existing) === sha256(content)) {
|
|
131
|
+
return { status: 'unchanged', path: finalPath };
|
|
132
|
+
}
|
|
133
|
+
if (!looksLikeLiteScript(existing)) {
|
|
134
|
+
const label = opts.label ? ` (${opts.label})` : '';
|
|
135
|
+
console.warn(
|
|
136
|
+
`Refusing to overwrite ${finalPath}${label}: existing file is not a clawmini-lite script`
|
|
137
|
+
);
|
|
138
|
+
return { status: 'refused', path: finalPath };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const dir = path.dirname(finalPath);
|
|
143
|
+
await fs.mkdir(dir, { recursive: true });
|
|
144
|
+
await fs.writeFile(finalPath, content, { mode: 0o755 });
|
|
145
|
+
return { status: 'written', path: finalPath };
|
|
146
|
+
}
|
|
147
|
+
|
|
80
148
|
export async function exportLiteToEnvironment(
|
|
81
149
|
envName: string,
|
|
82
150
|
envConfig: Environment,
|
|
@@ -98,8 +166,14 @@ export async function exportLiteToEnvironment(
|
|
|
98
166
|
}
|
|
99
167
|
|
|
100
168
|
try {
|
|
101
|
-
const
|
|
102
|
-
|
|
169
|
+
const result = await refreshLiteAt(finalExportPath, { label: `Environment: ${envName}` });
|
|
170
|
+
if (result.status === 'written') {
|
|
171
|
+
console.log(
|
|
172
|
+
`Successfully exported clawmini-lite to ${result.path} (Environment: ${envName})`
|
|
173
|
+
);
|
|
174
|
+
} else if (result.status === 'refused') {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
103
177
|
return true;
|
|
104
178
|
} catch (err) {
|
|
105
179
|
console.error(
|
package/src/shared/policies.ts
CHANGED
|
@@ -6,10 +6,35 @@ export interface PolicyDefinition {
|
|
|
6
6
|
autoApprove?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
// Resolved policy config: built-ins merged in, `false` overrides stripped.
|
|
10
|
+
// This is what every consumer should see.
|
|
9
11
|
export interface PolicyConfig {
|
|
10
12
|
policies: Record<string, PolicyDefinition>;
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
// On-disk shape: `false` opts a built-in out, a definition opts in / overrides.
|
|
16
|
+
export interface PolicyConfigFile {
|
|
17
|
+
policies: Record<string, PolicyDefinition | false>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const BUILTIN_POLICY_SCRIPTS_DIR = '.clawmini/policy-scripts';
|
|
21
|
+
|
|
22
|
+
export const BUILTIN_POLICIES: Record<string, PolicyDefinition> = {
|
|
23
|
+
'manage-policies': {
|
|
24
|
+
description:
|
|
25
|
+
'Add, update, or remove clawmini policies (subcommands: add, update, remove). Reads are unrestricted via `requests show`.',
|
|
26
|
+
command: `./${BUILTIN_POLICY_SCRIPTS_DIR}/manage-policies.js`,
|
|
27
|
+
allowHelp: true,
|
|
28
|
+
autoApprove: false,
|
|
29
|
+
},
|
|
30
|
+
'run-host': {
|
|
31
|
+
description: 'Run an arbitrary shell command on the host via `sh -c`',
|
|
32
|
+
command: `./${BUILTIN_POLICY_SCRIPTS_DIR}/run-host.js`,
|
|
33
|
+
allowHelp: true,
|
|
34
|
+
autoApprove: false,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
13
38
|
export type RequestState = 'Pending' | 'Approved' | 'Rejected';
|
|
14
39
|
|
|
15
40
|
export interface PolicyRequest {
|
|
@@ -17,6 +42,7 @@ export interface PolicyRequest {
|
|
|
17
42
|
commandName: string;
|
|
18
43
|
args: string[];
|
|
19
44
|
fileMappings: Record<string, string>;
|
|
45
|
+
cwd?: string;
|
|
20
46
|
state: RequestState;
|
|
21
47
|
createdAt: number;
|
|
22
48
|
rejectionReason?: string;
|