vellum 0.2.0 → 0.2.2
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/README.md +15 -2
- package/bun.lock +5 -2
- package/package.json +4 -2
- package/scripts/capture-x-graphql.ts +562 -0
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
- package/scripts/test.sh +5 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +161 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- package/src/__tests__/app-bundler.test.ts +12 -33
- package/src/__tests__/asset-materialize-tool.test.ts +16 -15
- package/src/__tests__/asset-search-tool.test.ts +23 -22
- package/src/__tests__/attachments-store.test.ts +56 -127
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
- package/src/__tests__/browser-skill-endstate.test.ts +5 -8
- package/src/__tests__/call-bridge.test.ts +385 -0
- package/src/__tests__/call-constants.test.ts +40 -0
- package/src/__tests__/call-orchestrator.test.ts +454 -0
- package/src/__tests__/call-recovery.test.ts +518 -0
- package/src/__tests__/call-routes-http.test.ts +459 -0
- package/src/__tests__/call-state-machine.test.ts +143 -0
- package/src/__tests__/call-state.test.ts +133 -0
- package/src/__tests__/call-store.test.ts +691 -0
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
- package/src/__tests__/compaction.benchmark.test.ts +176 -0
- package/src/__tests__/computer-use-tools.test.ts +250 -0
- package/src/__tests__/config-schema.test.ts +348 -3
- package/src/__tests__/conflict-store.test.ts +2 -1
- package/src/__tests__/contacts-tools.test.ts +331 -0
- package/src/__tests__/conversation-store.test.ts +30 -32
- package/src/__tests__/credential-security-invariants.test.ts +4 -0
- package/src/__tests__/date-context.test.ts +373 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
- package/src/__tests__/doordash-session.test.ts +9 -0
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
- package/src/__tests__/followup-tools.test.ts +303 -0
- package/src/__tests__/handlers-twitter-config.test.ts +718 -0
- package/src/__tests__/intent-routing.test.ts +64 -57
- package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
- package/src/__tests__/ipc-snapshot.test.ts +96 -28
- package/src/__tests__/llm-usage-store.test.ts +3 -8
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
- package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
- package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
- package/src/__tests__/playbook-tools.test.ts +342 -0
- package/src/__tests__/profile-compiler.test.ts +2 -1
- package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
- package/src/__tests__/recurrence-engine.test.ts +69 -0
- package/src/__tests__/recurrence-types.test.ts +71 -0
- package/src/__tests__/registry.test.ts +17 -10
- package/src/__tests__/relay-server.test.ts +633 -0
- package/src/__tests__/reminder-store.test.ts +6 -3
- package/src/__tests__/reminder.test.ts +43 -77
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +222 -0
- package/src/__tests__/run-orchestrator.test.ts +7 -7
- package/src/__tests__/runtime-attachment-metadata.test.ts +19 -20
- package/src/__tests__/runtime-runs-http.test.ts +5 -23
- package/src/__tests__/runtime-runs.test.ts +11 -11
- package/src/__tests__/schedule-store.test.ts +482 -0
- package/src/__tests__/schedule-tools.test.ts +700 -0
- package/src/__tests__/scheduler-recurrence.test.ts +329 -0
- package/src/__tests__/server-history-render.test.ts +14 -13
- package/src/__tests__/session-error.test.ts +28 -0
- package/src/__tests__/session-init.benchmark.test.ts +462 -0
- package/src/__tests__/session-queue.test.ts +89 -16
- package/src/__tests__/session-runtime-assembly.test.ts +161 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
- package/src/__tests__/signup-e2e.test.ts +2 -1
- package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
- package/src/__tests__/skill-script-runner.test.ts +159 -0
- package/src/__tests__/speaker-identification.test.ts +52 -0
- package/src/__tests__/subagent-manager-notify.test.ts +42 -10
- package/src/__tests__/subagent-tools.test.ts +141 -41
- package/src/__tests__/task-compiler.test.ts +2 -1
- package/src/__tests__/task-runner.test.ts +2 -1
- package/src/__tests__/task-scheduler.test.ts +2 -1
- package/src/__tests__/task-tools.test.ts +49 -56
- package/src/__tests__/tool-audit-listener.test.ts +1 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
- package/src/__tests__/tool-executor.test.ts +13 -17
- package/src/__tests__/turn-commit.test.ts +273 -2
- package/src/__tests__/twilio-provider.test.ts +143 -0
- package/src/__tests__/twilio-routes.test.ts +789 -0
- package/src/__tests__/twitter-auth-handler.test.ts +581 -0
- package/src/__tests__/view-image-tool.test.ts +217 -0
- package/src/__tests__/workspace-git-service.test.ts +403 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +141 -2
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +35 -14
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +48 -0
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +390 -0
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-state.ts +64 -0
- package/src/calls/call-store.ts +416 -0
- package/src/calls/relay-server.ts +335 -0
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-config.ts +34 -0
- package/src/calls/twilio-provider.ts +173 -0
- package/src/calls/twilio-routes.ts +250 -0
- package/src/calls/types.ts +37 -0
- package/src/calls/voice-provider.ts +14 -0
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +256 -25
- package/src/cli/ipc-client.ts +82 -0
- package/src/cli/map.ts +246 -0
- package/src/cli/twitter.ts +575 -0
- package/src/cli.ts +7 -5
- package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
- package/src/commands/cc-command-registry.ts +209 -0
- package/src/config/bundled-skills/contacts/SKILL.md +39 -0
- package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
- package/src/config/bundled-skills/document/SKILL.md +18 -0
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
- package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
- package/src/config/bundled-skills/doordash/SKILL.md +163 -0
- package/src/config/bundled-skills/followups/SKILL.md +32 -0
- package/src/config/bundled-skills/followups/TOOLS.json +100 -0
- package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
- package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -24
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
- package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
- package/src/config/bundled-skills/reminder/SKILL.md +20 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
- package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
- package/src/config/bundled-skills/schedule/SKILL.md +74 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
- package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
- package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
- package/src/config/bundled-skills/subagent/SKILL.md +25 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
- package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
- package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
- package/src/config/bundled-skills/tasks/SKILL.md +28 -0
- package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
- package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
- package/src/config/bundled-skills/twitter/SKILL.md +134 -0
- package/src/config/bundled-skills/watcher/SKILL.md +27 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
- package/src/config/defaults.ts +44 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +218 -1
- package/src/config/system-prompt.ts +100 -6
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +5 -0
- package/src/contacts/contact-store.ts +4 -4
- package/src/daemon/assistant-attachments.ts +10 -0
- package/src/daemon/classifier.ts +3 -1
- package/src/daemon/computer-use-session.ts +3 -1
- package/src/daemon/date-context.ts +136 -0
- package/src/daemon/handlers/apps.ts +16 -1
- package/src/daemon/handlers/browser.ts +54 -0
- package/src/daemon/handlers/computer-use.ts +7 -1
- package/src/daemon/handlers/config.ts +192 -4
- package/src/daemon/handlers/diagnostics.ts +5 -1
- package/src/daemon/handlers/documents.ts +18 -29
- package/src/daemon/handlers/home-base.ts +5 -1
- package/src/daemon/handlers/index.ts +40 -271
- package/src/daemon/handlers/misc.ts +9 -1
- package/src/daemon/handlers/publish.ts +6 -1
- package/src/daemon/handlers/sessions.ts +65 -12
- package/src/daemon/handlers/shared.ts +36 -1
- package/src/daemon/handlers/signing.ts +37 -0
- package/src/daemon/handlers/skills.ts +20 -6
- package/src/daemon/handlers/subagents.ts +8 -3
- package/src/daemon/handlers/twitter-auth.ts +169 -0
- package/src/daemon/handlers/work-items.ts +495 -39
- package/src/daemon/ipc-contract-inventory.json +40 -4
- package/src/daemon/ipc-contract.ts +185 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +48 -5
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +144 -29
- package/src/daemon/session-agent-loop.ts +887 -0
- package/src/daemon/session-attachments.ts +28 -5
- package/src/daemon/session-error.ts +24 -3
- package/src/daemon/session-lifecycle.ts +147 -0
- package/src/daemon/session-media-retry.ts +147 -0
- package/src/daemon/session-messaging.ts +145 -0
- package/src/daemon/session-notifiers.ts +164 -0
- package/src/daemon/session-process.ts +2 -2
- package/src/daemon/session-queue-manager.ts +1 -0
- package/src/daemon/session-runtime-assembly.ts +52 -0
- package/src/daemon/session-skill-tools.ts +124 -5
- package/src/daemon/session-slash.ts +3 -0
- package/src/daemon/session-surfaces.ts +77 -2
- package/src/daemon/session-tool-setup.ts +222 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1365
- package/src/daemon/video-thumbnail.ts +60 -0
- package/src/doordash/client.ts +121 -27
- package/src/doordash/queries.ts +1 -2
- package/src/export/formatter.ts +3 -1
- package/src/followups/followup-store.ts +4 -2
- package/src/followups/types.ts +6 -0
- package/src/hooks/templates.ts +1 -1
- package/src/index.ts +32 -1151
- package/src/media/gemini-image-service.ts +1 -1
- package/src/memory/attachments-store.ts +28 -83
- package/src/memory/channel-delivery-store.ts +7 -21
- package/src/memory/clarification-resolver.ts +6 -5
- package/src/memory/contradiction-checker.ts +3 -2
- package/src/memory/conversation-key-store.ts +10 -29
- package/src/memory/conversation-store.ts +2 -1
- package/src/memory/db.ts +362 -2
- package/src/memory/entity-extractor.ts +6 -3
- package/src/memory/items-extractor.ts +5 -4
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/llm-usage-store.ts +1 -2
- package/src/memory/runs-store.ts +1 -2
- package/src/memory/schema.ts +65 -2
- package/src/messaging/style-analyzer.ts +3 -2
- package/src/messaging/thread-summarizer.ts +8 -12
- package/src/messaging/triage-engine.ts +4 -2
- package/src/providers/openrouter/client.ts +20 -0
- package/src/providers/registry.ts +8 -0
- package/src/runtime/http-server.ts +277 -25
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/routes/attachment-routes.ts +5 -6
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +12 -19
- package/src/runtime/routes/conversation-routes.ts +5 -9
- package/src/runtime/routes/run-routes.ts +4 -8
- package/src/runtime/run-orchestrator.ts +39 -6
- package/src/schedule/recurrence-engine.ts +138 -0
- package/src/schedule/recurrence-types.ts +67 -0
- package/src/schedule/schedule-store.ts +102 -57
- package/src/schedule/scheduler.ts +9 -6
- package/src/security/oauth2.ts +29 -4
- package/src/security/secret-allowlist.ts +46 -0
- package/src/skills/clawhub.ts +1 -1
- package/src/subagent/manager.ts +40 -8
- package/src/swarm/backend-claude-code.ts +64 -9
- package/src/swarm/worker-prompts.ts +2 -1
- package/src/tasks/SPEC.md +34 -28
- package/src/tasks/ephemeral-permissions.ts +16 -7
- package/src/tasks/task-compiler.ts +5 -4
- package/src/tasks/task-runner.ts +10 -5
- package/src/tasks/task-scheduler.ts +1 -1
- package/src/tasks/tool-sanitizer.ts +36 -0
- package/src/tools/assets/search.ts +4 -4
- package/src/tools/browser/api-map.ts +220 -0
- package/src/tools/browser/auto-navigate.ts +270 -0
- package/src/tools/browser/browser-execution.ts +2 -1
- package/src/tools/browser/browser-manager.ts +2 -2
- package/src/tools/browser/network-recorder.ts +5 -4
- package/src/tools/browser/x-auto-navigate.ts +207 -0
- package/src/tools/calls/call-end.ts +67 -0
- package/src/tools/calls/call-start.ts +73 -0
- package/src/tools/calls/call-status.ts +81 -0
- package/src/tools/claude-code/claude-code.ts +77 -11
- package/src/tools/contacts/contact-merge.ts +46 -78
- package/src/tools/contacts/contact-search.ts +35 -79
- package/src/tools/contacts/contact-upsert.ts +35 -108
- package/src/tools/credentials/vault.ts +21 -5
- package/src/tools/document/document-tool.ts +71 -144
- package/src/tools/executor.ts +129 -10
- package/src/tools/followups/followup_create.ts +46 -88
- package/src/tools/followups/followup_list.ts +34 -74
- package/src/tools/followups/followup_resolve.ts +31 -66
- package/src/tools/host-terminal/cli-discover.ts +2 -1
- package/src/tools/host-terminal/host-shell.ts +10 -0
- package/src/tools/memory/handlers.ts +5 -4
- package/src/tools/network/__tests__/web-search.test.ts +427 -0
- package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
- package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
- package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
- package/src/tools/network/web-fetch.ts +18 -6
- package/src/tools/playbooks/index.ts +4 -5
- package/src/tools/playbooks/playbook-create.ts +3 -47
- package/src/tools/playbooks/playbook-delete.ts +1 -25
- package/src/tools/playbooks/playbook-list.ts +1 -28
- package/src/tools/playbooks/playbook-update.ts +3 -51
- package/src/tools/registry.ts +2 -4
- package/src/tools/reminder/reminder.ts +5 -78
- package/src/tools/schedule/create.ts +69 -74
- package/src/tools/schedule/delete.ts +21 -47
- package/src/tools/schedule/list.ts +55 -74
- package/src/tools/schedule/update.ts +77 -84
- package/src/tools/subagent/abort.ts +29 -58
- package/src/tools/subagent/message.ts +30 -63
- package/src/tools/subagent/read.ts +53 -84
- package/src/tools/subagent/spawn.ts +43 -82
- package/src/tools/subagent/status.ts +42 -71
- package/src/tools/swarm/delegate.ts +2 -1
- package/src/tools/tasks/index.ts +8 -6
- package/src/tools/tasks/task-delete.ts +69 -56
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +74 -102
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +192 -134
- package/src/tools/tasks/work-item-list.ts +33 -78
- package/src/tools/tasks/work-item-remove.ts +60 -0
- package/src/tools/tasks/work-item-update.ts +114 -0
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +20 -74
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tools/watch/screen-watch.ts +3 -1
- package/src/tools/watcher/create.ts +52 -98
- package/src/tools/watcher/delete.ts +20 -46
- package/src/tools/watcher/digest.ts +36 -70
- package/src/tools/watcher/list.ts +49 -79
- package/src/tools/watcher/update.ts +45 -91
- package/src/twitter/client.ts +690 -0
- package/src/twitter/session.ts +91 -0
- package/src/usage/types.ts +0 -1
- package/src/util/truncate.ts +6 -0
- package/src/watcher/providers/slack.ts +2 -1
- package/src/watcher/watcher-store.ts +3 -2
- package/src/work-items/work-item-store.ts +236 -2
- package/src/workspace/commit-message-enrichment-service.ts +284 -0
- package/src/workspace/commit-message-provider.ts +95 -0
- package/src/workspace/git-service.ts +272 -52
- package/src/workspace/heartbeat-service.ts +70 -13
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +100 -51
- package/src/tools/contacts/index.ts +0 -4
- package/src/tools/document/index.ts +0 -5
- package/src/tools/followups/index.ts +0 -3
- package/src/tools/subagent/index.ts +0 -5
- /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { isValidScheduleExpression, computeNextRunAt } from '../schedule/recurrence-engine.js';
|
|
3
|
+
|
|
4
|
+
describe('RRULE set engine support', () => {
|
|
5
|
+
test('multiple RRULE lines are unioned — next run is earliest', () => {
|
|
6
|
+
// Daily at 9am + Weekly on Mondays at 3pm, starting Jan 1 2099
|
|
7
|
+
const expr = [
|
|
8
|
+
'DTSTART:20990101T090000Z',
|
|
9
|
+
'RRULE:FREQ=DAILY;BYHOUR=9;BYMINUTE=0;BYSECOND=0',
|
|
10
|
+
'RRULE:FREQ=WEEKLY;BYDAY=MO;BYHOUR=15;BYMINUTE=0;BYSECOND=0',
|
|
11
|
+
].join('\n');
|
|
12
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
13
|
+
const next = computeNextRunAt({ syntax: 'rrule', expression: expr });
|
|
14
|
+
expect(next).toBeGreaterThan(Date.now());
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('RRULE + EXDATE excludes matching occurrence', () => {
|
|
18
|
+
// Daily starting Jan 1 2099, exclude Jan 2
|
|
19
|
+
const expr = [
|
|
20
|
+
'DTSTART:20990101T090000Z',
|
|
21
|
+
'RRULE:FREQ=DAILY;COUNT=5',
|
|
22
|
+
'EXDATE:20990102T090000Z',
|
|
23
|
+
].join('\n');
|
|
24
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
25
|
+
// First occurrence: Jan 1, second should skip Jan 2 and be Jan 3
|
|
26
|
+
const jan1 = new Date('2099-01-01T09:00:00Z').getTime();
|
|
27
|
+
const jan2 = new Date('2099-01-02T09:00:00Z').getTime();
|
|
28
|
+
const next = computeNextRunAt({ syntax: 'rrule', expression: expr }, jan1 + 1);
|
|
29
|
+
// Should not be Jan 2 (excluded)
|
|
30
|
+
expect(next).not.toBe(jan2);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('RDATE adds ad-hoc occurrence', () => {
|
|
34
|
+
// Weekly on Mondays + an extra occurrence on Jan 15 2099 (Wednesday)
|
|
35
|
+
const expr = [
|
|
36
|
+
'DTSTART:20990106T090000Z',
|
|
37
|
+
'RRULE:FREQ=WEEKLY;BYDAY=MO',
|
|
38
|
+
'RDATE:20990115T090000Z',
|
|
39
|
+
].join('\n');
|
|
40
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('unknown line is rejected', () => {
|
|
44
|
+
const expr = [
|
|
45
|
+
'DTSTART:20990101T090000Z',
|
|
46
|
+
'RRULE:FREQ=DAILY',
|
|
47
|
+
'SUMMARY:My event',
|
|
48
|
+
].join('\n');
|
|
49
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('expression without DTSTART is rejected', () => {
|
|
53
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: 'RRULE:FREQ=DAILY' })).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('expression without inclusion source is rejected', () => {
|
|
57
|
+
const expr = 'DTSTART:20990101T090000Z\nEXDATE:20990102T090000Z';
|
|
58
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('escaped newlines are normalized', () => {
|
|
62
|
+
const expr = 'DTSTART:20990101T090000Z\\nRRULE:FREQ=DAILY';
|
|
63
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
64
|
+
const next = computeNextRunAt({ syntax: 'rrule', expression: expr });
|
|
65
|
+
expect(next).toBeGreaterThan(Date.now());
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('existing single RRULE still works', () => {
|
|
69
|
+
const expr = 'DTSTART:20990101T090000Z\nRRULE:FREQ=DAILY';
|
|
70
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('existing cron still works', () => {
|
|
74
|
+
expect(isValidScheduleExpression({ syntax: 'cron', expression: '0 9 * * 1-5' })).toBe(true);
|
|
75
|
+
const next = computeNextRunAt({ syntax: 'cron', expression: '* * * * *' });
|
|
76
|
+
expect(next).toBeGreaterThan(Date.now() - 1);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { isValidScheduleExpression, computeNextRunAt } from '../schedule/recurrence-engine.js';
|
|
3
|
+
|
|
4
|
+
describe('recurrence engine — cron', () => {
|
|
5
|
+
test('validates valid cron expressions', () => {
|
|
6
|
+
expect(isValidScheduleExpression({ syntax: 'cron', expression: '0 9 * * 1-5' })).toBe(true);
|
|
7
|
+
expect(isValidScheduleExpression({ syntax: 'cron', expression: '*/5 * * * *' })).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('rejects invalid cron expressions', () => {
|
|
11
|
+
expect(isValidScheduleExpression({ syntax: 'cron', expression: 'not valid' })).toBe(false);
|
|
12
|
+
expect(isValidScheduleExpression({ syntax: 'cron', expression: '' })).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('computes next run for cron', () => {
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
const next = computeNextRunAt({ syntax: 'cron', expression: '* * * * *' }, now);
|
|
18
|
+
expect(next).toBeGreaterThan(now - 1);
|
|
19
|
+
// Next minute should be within 60 seconds
|
|
20
|
+
expect(next - now).toBeLessThanOrEqual(60_000);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('recurrence engine — rrule', () => {
|
|
25
|
+
test('validates RRULE with DTSTART', () => {
|
|
26
|
+
const expr = 'DTSTART:20250101T090000Z\nRRULE:FREQ=DAILY;BYHOUR=9;BYMINUTE=0';
|
|
27
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: expr })).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('rejects RRULE without DTSTART', () => {
|
|
31
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: 'RRULE:FREQ=DAILY' })).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('accepts RRULE set constructs', () => {
|
|
35
|
+
const withExdate = 'DTSTART:20250101T090000Z\nRRULE:FREQ=DAILY\nEXDATE:20250105T090000Z';
|
|
36
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: withExdate })).toBe(true);
|
|
37
|
+
|
|
38
|
+
const withRdate = 'DTSTART:20250101T090000Z\nRRULE:FREQ=DAILY\nRDATE:20250115T090000Z';
|
|
39
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: withRdate })).toBe(true);
|
|
40
|
+
|
|
41
|
+
const multiRrule = 'DTSTART:20250101T090000Z\nRRULE:FREQ=DAILY\nRRULE:FREQ=WEEKLY';
|
|
42
|
+
expect(isValidScheduleExpression({ syntax: 'rrule', expression: multiRrule })).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('computes next run for RRULE with future DTSTART', () => {
|
|
46
|
+
// Use a date far in the future to ensure the test is stable
|
|
47
|
+
const futureDate = new Date('2099-01-01T09:00:00Z');
|
|
48
|
+
const expr = 'DTSTART:20990101T090000Z\nRRULE:FREQ=DAILY';
|
|
49
|
+
const next = computeNextRunAt({ syntax: 'rrule', expression: expr });
|
|
50
|
+
expect(next).toBeGreaterThanOrEqual(futureDate.getTime());
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('throws when RRULE has no future runs (UNTIL in past)', () => {
|
|
54
|
+
const expr = 'DTSTART:20200101T090000Z\nRRULE:FREQ=DAILY;UNTIL=20200105T090000Z';
|
|
55
|
+
expect(() => computeNextRunAt({ syntax: 'rrule', expression: expr })).toThrow(/no upcoming runs/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('throws for RRULE missing DTSTART in computeNextRunAt', () => {
|
|
59
|
+
expect(() => computeNextRunAt({ syntax: 'rrule', expression: 'RRULE:FREQ=DAILY' })).toThrow(/DTSTART/);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('computes next run for RRULE with EXDATE set construct', () => {
|
|
63
|
+
const expr = 'DTSTART:20990101T090000Z\nRRULE:FREQ=DAILY\nEXDATE:20990101T090000Z';
|
|
64
|
+
const next = computeNextRunAt({ syntax: 'rrule', expression: expr });
|
|
65
|
+
// Should skip the excluded date and return January 2
|
|
66
|
+
const jan2 = new Date('2099-01-02T09:00:00Z').getTime();
|
|
67
|
+
expect(next).toBe(jan2);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, test, expect } from 'bun:test';
|
|
2
|
+
import { detectScheduleSyntax, normalizeScheduleSyntax } from '../schedule/recurrence-types.js';
|
|
3
|
+
|
|
4
|
+
describe('detectScheduleSyntax', () => {
|
|
5
|
+
test('detects cron for standard 5-field expressions', () => {
|
|
6
|
+
expect(detectScheduleSyntax('0 9 * * 1-5')).toBe('cron');
|
|
7
|
+
expect(detectScheduleSyntax('*/5 * * * *')).toBe('cron');
|
|
8
|
+
expect(detectScheduleSyntax('30 14 1 * *')).toBe('cron');
|
|
9
|
+
expect(detectScheduleSyntax('0 0 * * 0')).toBe('cron');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('detects RRULE for RRULE: prefix', () => {
|
|
13
|
+
expect(detectScheduleSyntax('RRULE:FREQ=DAILY;BYHOUR=9;BYMINUTE=0')).toBe('rrule');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('detects RRULE for DTSTART + RRULE multiline', () => {
|
|
17
|
+
expect(detectScheduleSyntax('DTSTART:20250101T090000Z\nRRULE:FREQ=DAILY')).toBe('rrule');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('detects RRULE for expression containing FREQ=', () => {
|
|
21
|
+
expect(detectScheduleSyntax('FREQ=WEEKLY;BYDAY=MO,WE,FR')).toBe('rrule');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('returns null for empty/invalid input', () => {
|
|
25
|
+
expect(detectScheduleSyntax('')).toBeNull();
|
|
26
|
+
expect(detectScheduleSyntax(' ')).toBeNull();
|
|
27
|
+
expect(detectScheduleSyntax('hello world')).toBeNull();
|
|
28
|
+
expect(detectScheduleSyntax('not a schedule expression at all and is long')).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('returns null for ambiguous expressions', () => {
|
|
32
|
+
// Single word that is not 5 fields and not RRULE
|
|
33
|
+
expect(detectScheduleSyntax('daily')).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('normalizeScheduleSyntax', () => {
|
|
38
|
+
test('uses explicit syntax + expression when both provided', () => {
|
|
39
|
+
const result = normalizeScheduleSyntax({
|
|
40
|
+
syntax: 'rrule',
|
|
41
|
+
expression: 'RRULE:FREQ=DAILY',
|
|
42
|
+
});
|
|
43
|
+
expect(result).toEqual({ syntax: 'rrule', expression: 'RRULE:FREQ=DAILY' });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('auto-detects syntax from expression', () => {
|
|
47
|
+
const cronResult = normalizeScheduleSyntax({ expression: '0 9 * * 1-5' });
|
|
48
|
+
expect(cronResult).toEqual({ syntax: 'cron', expression: '0 9 * * 1-5' });
|
|
49
|
+
|
|
50
|
+
const rruleResult = normalizeScheduleSyntax({ expression: 'RRULE:FREQ=DAILY' });
|
|
51
|
+
expect(rruleResult).toEqual({ syntax: 'rrule', expression: 'RRULE:FREQ=DAILY' });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('falls back to legacyCronExpression', () => {
|
|
55
|
+
const result = normalizeScheduleSyntax({ legacyCronExpression: '0 9 * * *' });
|
|
56
|
+
expect(result).toEqual({ syntax: 'cron', expression: '0 9 * * *' });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('returns null when nothing is provided', () => {
|
|
60
|
+
expect(normalizeScheduleSyntax({})).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('returns null when expression is ambiguous and no syntax hint', () => {
|
|
64
|
+
expect(normalizeScheduleSyntax({ expression: 'daily' })).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('trusts explicit syntax when auto-detection fails', () => {
|
|
68
|
+
const result = normalizeScheduleSyntax({ syntax: 'cron', expression: 'some-custom-expr' });
|
|
69
|
+
expect(result).toEqual({ syntax: 'cron', expression: 'some-custom-expr' });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
__resetRegistryForTesting,
|
|
20
20
|
__clearRegistryForTesting,
|
|
21
21
|
} from '../tools/registry.js';
|
|
22
|
-
import {
|
|
22
|
+
import { eagerModuleToolNames, explicitTools, lazyTools } from '../tools/tool-manifest.js';
|
|
23
23
|
|
|
24
24
|
// Clean up global registry after this file completes to prevent
|
|
25
25
|
// contamination of subsequent test files in combined runs.
|
|
@@ -175,24 +175,26 @@ describe('tool manifest', () => {
|
|
|
175
175
|
expect(lazyNames.has('swarm_delegate')).toBe(true);
|
|
176
176
|
});
|
|
177
177
|
|
|
178
|
-
test('eager module list contains expected count', () => {
|
|
179
|
-
expect(
|
|
178
|
+
test('eager module tool names list contains expected count', () => {
|
|
179
|
+
expect(eagerModuleToolNames.length).toBe(15);
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
test('explicit tools list includes memory, credential, and
|
|
182
|
+
test('explicit tools list includes memory, credential, watch, catalog, and discover tools', () => {
|
|
183
183
|
const names = explicitTools.map((t) => t.name);
|
|
184
184
|
expect(names).toContain('memory_search');
|
|
185
185
|
expect(names).toContain('memory_save');
|
|
186
186
|
expect(names).toContain('memory_update');
|
|
187
187
|
expect(names).toContain('credential_store');
|
|
188
188
|
expect(names).toContain('account_manage');
|
|
189
|
-
expect(names).toContain('
|
|
189
|
+
expect(names).toContain('start_screen_watch');
|
|
190
|
+
expect(names).toContain('vellum_skills_catalog');
|
|
191
|
+
expect(names).toContain('cli_discover');
|
|
190
192
|
});
|
|
191
193
|
|
|
192
194
|
test('registered tool count is at least eager + lazy + host', async () => {
|
|
193
195
|
await initializeTools();
|
|
194
196
|
const tools = getAllTools();
|
|
195
|
-
expect(tools.length).toBeGreaterThanOrEqual(
|
|
197
|
+
expect(tools.length).toBeGreaterThanOrEqual(eagerModuleToolNames.length + lazyTools.length);
|
|
196
198
|
});
|
|
197
199
|
});
|
|
198
200
|
|
|
@@ -210,8 +212,13 @@ describe('baseline characterization: hardcoded tool loading', () => {
|
|
|
210
212
|
}
|
|
211
213
|
});
|
|
212
214
|
|
|
213
|
-
test('gmail
|
|
214
|
-
|
|
215
|
+
test('gmail tool names are NOT in eagerModuleToolNames manifest', () => {
|
|
216
|
+
const gmailTools = ['gmail_search', 'gmail_list_messages', 'gmail_get_message', 'gmail_mark_read',
|
|
217
|
+
'gmail_draft', 'gmail_archive', 'gmail_batch_archive', 'gmail_label', 'gmail_batch_label',
|
|
218
|
+
'gmail_trash', 'gmail_send', 'gmail_unsubscribe'];
|
|
219
|
+
for (const name of gmailTools) {
|
|
220
|
+
expect(eagerModuleToolNames).not.toContain(name);
|
|
221
|
+
}
|
|
215
222
|
});
|
|
216
223
|
|
|
217
224
|
test('weather tool is NOT in global registry after initializeTools()', async () => {
|
|
@@ -220,8 +227,8 @@ describe('baseline characterization: hardcoded tool loading', () => {
|
|
|
220
227
|
expect(tool).toBeUndefined();
|
|
221
228
|
});
|
|
222
229
|
|
|
223
|
-
test('weather
|
|
224
|
-
expect(
|
|
230
|
+
test('weather tool name is NOT in eagerModuleToolNames manifest', () => {
|
|
231
|
+
expect(eagerModuleToolNames).not.toContain('get_weather');
|
|
225
232
|
});
|
|
226
233
|
|
|
227
234
|
test('claude_code is NOT in global registry after initializeTools()', async () => {
|