vellum 0.2.1 → 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 +133 -34
- package/src/__tests__/account-registry.test.ts +2 -1
- package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
- 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 +4 -3
- 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 +130 -4
- 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-store.test.ts +216 -1
- package/src/__tests__/cli-discover.test.ts +1 -1
- package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
- 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 +299 -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__/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 +62 -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 +5 -3
- 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 +8 -4
- package/src/__tests__/run-orchestrator.test.ts +4 -4
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
- package/src/__tests__/runtime-runs-http.test.ts +4 -4
- package/src/__tests__/runtime-runs.test.ts +4 -4
- 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 +71 -48
- 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 +218 -3
- 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 +186 -0
- package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
- package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
- package/src/bundler/app-bundler.ts +12 -8
- package/src/calls/call-bridge.ts +95 -0
- package/src/calls/call-constants.ts +43 -5
- package/src/calls/call-domain.ts +276 -0
- package/src/calls/call-orchestrator.ts +43 -17
- package/src/calls/call-recovery.ts +207 -0
- package/src/calls/call-state-machine.ts +68 -0
- package/src/calls/call-store.ts +192 -5
- package/src/calls/relay-server.ts +41 -4
- package/src/calls/speaker-identification.ts +213 -0
- package/src/calls/twilio-provider.ts +10 -6
- package/src/calls/twilio-routes.ts +90 -76
- package/src/calls/types.ts +1 -1
- package/src/cli/config-commands.ts +334 -0
- package/src/cli/core-commands.ts +776 -0
- package/src/cli/doordash.ts +251 -1
- 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 +82 -23
- 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/media-generate-image.ts +1 -23
- 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 +33 -0
- package/src/config/loader.ts +4 -1
- package/src/config/schema.ts +161 -1
- package/src/config/system-prompt.ts +61 -16
- package/src/config/templates/IDENTITY.md +7 -0
- package/src/config/types.ts +4 -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 +163 -5
- 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 -277
- 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 +384 -68
- package/src/daemon/ipc-contract-inventory.json +28 -4
- package/src/daemon/ipc-contract.ts +133 -37
- package/src/daemon/ipc-protocol.ts +7 -2
- package/src/daemon/lifecycle.ts +21 -0
- package/src/daemon/main.ts +10 -4
- package/src/daemon/ride-shotgun-handler.ts +74 -10
- package/src/daemon/server.ts +143 -26
- 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 +216 -2
- package/src/daemon/session-usage.ts +0 -2
- package/src/daemon/session.ts +114 -1404
- 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 -1153
- 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 +96 -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 +23 -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 +108 -20
- package/src/runtime/routes/attachment-routes.ts +2 -3
- package/src/runtime/routes/call-routes.ts +140 -0
- package/src/runtime/routes/channel-routes.ts +5 -10
- package/src/runtime/routes/conversation-routes.ts +5 -5
- package/src/runtime/routes/run-routes.ts +2 -2
- package/src/runtime/run-orchestrator.ts +9 -3
- 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 +17 -67
- package/src/tools/calls/call-start.ts +24 -85
- package/src/tools/calls/call-status.ts +35 -51
- 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 +20 -4
- 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/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 -8
- package/src/tools/tasks/task-delete.ts +60 -88
- package/src/tools/tasks/task-list.ts +31 -52
- package/src/tools/tasks/task-run.ts +72 -108
- package/src/tools/tasks/task-save.ts +33 -65
- package/src/tools/tasks/work-item-enqueue.ts +183 -215
- package/src/tools/tasks/work-item-list.ts +33 -63
- package/src/tools/tasks/work-item-remove.ts +45 -97
- package/src/tools/tasks/work-item-update.ts +91 -163
- package/src/tools/terminal/backends/native.ts +3 -1
- package/src/tools/tool-manifest.ts +0 -62
- package/src/tools/types.ts +6 -0
- package/src/tools/ui-surface/definitions.ts +3 -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 +27 -2
- package/src/workspace/commit-message-enrichment-service.ts +31 -7
- package/src/workspace/git-service.ts +87 -22
- package/src/workspace/provider-commit-message-generator.ts +242 -0
- package/src/workspace/turn-commit.ts +62 -3
- 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,242 @@
|
|
|
1
|
+
import { getLogger } from '../util/logger.js';
|
|
2
|
+
import { getConfig } from '../config/loader.js';
|
|
3
|
+
import type { CommitContext } from './commit-message-provider.js';
|
|
4
|
+
import { DefaultCommitMessageProvider } from './commit-message-provider.js';
|
|
5
|
+
import type { Message } from '../providers/types.js';
|
|
6
|
+
|
|
7
|
+
const log = getLogger('commit-message-llm');
|
|
8
|
+
|
|
9
|
+
export type CommitMessageSource = 'llm' | 'deterministic';
|
|
10
|
+
export type LLMFallbackReason =
|
|
11
|
+
| 'disabled'
|
|
12
|
+
| 'provider_not_initialized'
|
|
13
|
+
| 'breaker_open'
|
|
14
|
+
| 'insufficient_budget'
|
|
15
|
+
| 'timeout'
|
|
16
|
+
| 'provider_error'
|
|
17
|
+
| 'invalid_output';
|
|
18
|
+
|
|
19
|
+
export interface GenerateCommitMessageResult {
|
|
20
|
+
message: string;
|
|
21
|
+
source: CommitMessageSource;
|
|
22
|
+
reason?: LLMFallbackReason;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface GenerateOptions {
|
|
26
|
+
deadlineMs?: number;
|
|
27
|
+
changedFiles: string[];
|
|
28
|
+
diffSummary?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const SYSTEM_PROMPT = `You generate concise git commit messages for workspace file changes.
|
|
32
|
+
Rules:
|
|
33
|
+
- Write a single short subject line (max 72 chars), optionally followed by a blank line and 2-4 concise bullet points
|
|
34
|
+
- No markdown headings or formatting
|
|
35
|
+
- Only mention files and changes actually provided
|
|
36
|
+
- Total output must be under 300 characters
|
|
37
|
+
- If you cannot determine a meaningful message, respond with exactly: FALLBACK`;
|
|
38
|
+
|
|
39
|
+
const deterministicProvider = new DefaultCommitMessageProvider();
|
|
40
|
+
|
|
41
|
+
function buildDeterministicResult(
|
|
42
|
+
context: CommitContext,
|
|
43
|
+
reason: LLMFallbackReason,
|
|
44
|
+
): GenerateCommitMessageResult {
|
|
45
|
+
return {
|
|
46
|
+
message: deterministicProvider.buildImmediateMessage(context).message,
|
|
47
|
+
source: 'deterministic',
|
|
48
|
+
reason,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class ProviderCommitMessageGenerator {
|
|
53
|
+
private consecutiveFailures = 0;
|
|
54
|
+
private nextAllowedAttemptMs = 0;
|
|
55
|
+
|
|
56
|
+
private isBreakerOpen(): boolean {
|
|
57
|
+
const config = getConfig();
|
|
58
|
+
const { openAfterFailures } = config.workspaceGit.commitMessageLLM.breaker;
|
|
59
|
+
if (this.consecutiveFailures < openAfterFailures) return false;
|
|
60
|
+
return Date.now() < this.nextAllowedAttemptMs;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private recordSuccess(): void {
|
|
64
|
+
if (this.consecutiveFailures > 0) {
|
|
65
|
+
log.info(
|
|
66
|
+
{ previousFailures: this.consecutiveFailures },
|
|
67
|
+
'Commit message LLM breaker closed: succeeded after failures',
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
this.consecutiveFailures = 0;
|
|
71
|
+
this.nextAllowedAttemptMs = 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private recordFailure(): void {
|
|
75
|
+
const config = getConfig();
|
|
76
|
+
const { backoffBaseMs, backoffMaxMs } = config.workspaceGit.commitMessageLLM.breaker;
|
|
77
|
+
this.consecutiveFailures++;
|
|
78
|
+
const delay = Math.min(
|
|
79
|
+
backoffBaseMs * Math.pow(2, this.consecutiveFailures - 1),
|
|
80
|
+
backoffMaxMs,
|
|
81
|
+
);
|
|
82
|
+
this.nextAllowedAttemptMs = Date.now() + delay;
|
|
83
|
+
log.warn(
|
|
84
|
+
{ consecutiveFailures: this.consecutiveFailures, backoffMs: delay },
|
|
85
|
+
'Commit message LLM breaker opened: backing off',
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async generateCommitMessage(
|
|
90
|
+
context: CommitContext,
|
|
91
|
+
options: GenerateOptions,
|
|
92
|
+
): Promise<GenerateCommitMessageResult> {
|
|
93
|
+
const config = getConfig();
|
|
94
|
+
const llmConfig = config.workspaceGit.commitMessageLLM;
|
|
95
|
+
|
|
96
|
+
// Step 1: Feature gate
|
|
97
|
+
if (!llmConfig.enabled) {
|
|
98
|
+
return buildDeterministicResult(context, 'disabled');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Step 2: Provider gate
|
|
102
|
+
if (!llmConfig.useConfiguredProvider) {
|
|
103
|
+
return buildDeterministicResult(context, 'disabled');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Step 3: Circuit breaker
|
|
107
|
+
if (this.isBreakerOpen()) {
|
|
108
|
+
log.debug(
|
|
109
|
+
{ consecutiveFailures: this.consecutiveFailures },
|
|
110
|
+
'Commit message LLM breaker open; falling back to deterministic',
|
|
111
|
+
);
|
|
112
|
+
return buildDeterministicResult(context, 'breaker_open');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Step 4: Budget check
|
|
116
|
+
if (options.deadlineMs !== undefined) {
|
|
117
|
+
const remaining = options.deadlineMs - Date.now();
|
|
118
|
+
if (remaining < llmConfig.minRemainingTurnBudgetMs) {
|
|
119
|
+
log.debug(
|
|
120
|
+
{ remainingMs: remaining, minBudgetMs: llmConfig.minRemainingTurnBudgetMs },
|
|
121
|
+
'Insufficient budget for LLM commit message',
|
|
122
|
+
);
|
|
123
|
+
return buildDeterministicResult(context, 'insufficient_budget');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Step 5: Call the provider
|
|
128
|
+
try {
|
|
129
|
+
const { getProvider } = await import('../providers/registry.js');
|
|
130
|
+
|
|
131
|
+
let provider;
|
|
132
|
+
try {
|
|
133
|
+
provider = getProvider(config.provider);
|
|
134
|
+
} catch {
|
|
135
|
+
log.debug({ provider: config.provider }, 'Provider not initialized; falling back to deterministic');
|
|
136
|
+
return buildDeterministicResult(context, 'provider_not_initialized');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Build prompt
|
|
140
|
+
const fileList = options.changedFiles
|
|
141
|
+
.slice(0, llmConfig.maxFilesInPrompt)
|
|
142
|
+
.join('\n');
|
|
143
|
+
const truncatedSuffix = options.changedFiles.length > llmConfig.maxFilesInPrompt
|
|
144
|
+
? `\n... and ${options.changedFiles.length - llmConfig.maxFilesInPrompt} more files`
|
|
145
|
+
: '';
|
|
146
|
+
|
|
147
|
+
let userText = `Changed files:\n${fileList}${truncatedSuffix}`;
|
|
148
|
+
if (options.diffSummary) {
|
|
149
|
+
const diffBytes = new TextEncoder().encode(options.diffSummary).length;
|
|
150
|
+
const diff = diffBytes > llmConfig.maxDiffBytes
|
|
151
|
+
? new TextDecoder().decode(new TextEncoder().encode(options.diffSummary).slice(0, llmConfig.maxDiffBytes)) + '\n... (truncated)'
|
|
152
|
+
: options.diffSummary;
|
|
153
|
+
userText += `\n\nDiff summary:\n${diff}`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const messages: Message[] = [
|
|
157
|
+
{
|
|
158
|
+
role: 'user',
|
|
159
|
+
content: [{ type: 'text', text: userText }],
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
// AbortController with timeout
|
|
164
|
+
const ac = new AbortController();
|
|
165
|
+
const timer = setTimeout(() => ac.abort(), llmConfig.timeoutMs);
|
|
166
|
+
|
|
167
|
+
let response;
|
|
168
|
+
try {
|
|
169
|
+
response = await provider.sendMessage(
|
|
170
|
+
messages,
|
|
171
|
+
undefined,
|
|
172
|
+
SYSTEM_PROMPT,
|
|
173
|
+
{
|
|
174
|
+
signal: ac.signal,
|
|
175
|
+
config: { max_tokens: llmConfig.maxTokens, temperature: llmConfig.temperature },
|
|
176
|
+
},
|
|
177
|
+
);
|
|
178
|
+
} catch (err: unknown) {
|
|
179
|
+
clearTimeout(timer);
|
|
180
|
+
if (ac.signal.aborted) {
|
|
181
|
+
log.warn('Commit message LLM timed out; falling back to deterministic');
|
|
182
|
+
this.recordFailure();
|
|
183
|
+
return buildDeterministicResult(context, 'timeout');
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
|
|
189
|
+
// Extract text from response
|
|
190
|
+
const textBlocks = response.content.filter((b) => b.type === 'text');
|
|
191
|
+
const text = textBlocks
|
|
192
|
+
.map((b) => (b as { type: 'text'; text: string }).text)
|
|
193
|
+
.join('')
|
|
194
|
+
.trim();
|
|
195
|
+
|
|
196
|
+
// Validate output
|
|
197
|
+
if (!text || text === 'FALLBACK' || text.length > 500) {
|
|
198
|
+
log.debug(
|
|
199
|
+
{ outputLength: text?.length ?? 0, isFallback: text === 'FALLBACK' },
|
|
200
|
+
'LLM output invalid; falling back to deterministic',
|
|
201
|
+
);
|
|
202
|
+
this.recordFailure();
|
|
203
|
+
return buildDeterministicResult(context, 'invalid_output');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Validate single-line subject: first line must be <= 72 chars
|
|
207
|
+
const firstLine = text.split('\n')[0];
|
|
208
|
+
if (firstLine.length > 72) {
|
|
209
|
+
log.debug(
|
|
210
|
+
{ subjectLength: firstLine.length },
|
|
211
|
+
'LLM subject line too long; falling back to deterministic',
|
|
212
|
+
);
|
|
213
|
+
this.recordFailure();
|
|
214
|
+
return buildDeterministicResult(context, 'invalid_output');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
this.recordSuccess();
|
|
218
|
+
return { message: text, source: 'llm' };
|
|
219
|
+
} catch (err: unknown) {
|
|
220
|
+
// Step 6: Any error -> deterministic fallback
|
|
221
|
+
log.warn(
|
|
222
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
223
|
+
'Commit message LLM provider error; falling back to deterministic',
|
|
224
|
+
);
|
|
225
|
+
this.recordFailure();
|
|
226
|
+
return buildDeterministicResult(context, 'provider_error');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let instance: ProviderCommitMessageGenerator | null = null;
|
|
232
|
+
|
|
233
|
+
export function getCommitMessageGenerator(): ProviderCommitMessageGenerator {
|
|
234
|
+
if (!instance) {
|
|
235
|
+
instance = new ProviderCommitMessageGenerator();
|
|
236
|
+
}
|
|
237
|
+
return instance;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function _resetCommitMessageGenerator(): void {
|
|
241
|
+
instance = null;
|
|
242
|
+
}
|
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
type CommitMessageProvider,
|
|
18
18
|
} from './commit-message-provider.js';
|
|
19
19
|
import { getEnrichmentService } from './commit-message-enrichment-service.js';
|
|
20
|
+
import { getCommitMessageGenerator } from './provider-commit-message-generator.js';
|
|
21
|
+
import type { CommitMessageSource, LLMFallbackReason } from './provider-commit-message-generator.js';
|
|
20
22
|
|
|
21
23
|
const log = getLogger('turn-commit');
|
|
22
24
|
|
|
@@ -58,8 +60,54 @@ export async function commitTurnChanges(
|
|
|
58
60
|
const gitService = getWorkspaceGitService(workspaceDir);
|
|
59
61
|
const commitStartMs = Date.now();
|
|
60
62
|
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
+
// Attempt LLM message generation BEFORE entering commitIfDirty so
|
|
64
|
+
// the LLM call never runs while holding the git mutex.
|
|
65
|
+
// Only attempt LLM when:
|
|
66
|
+
// 1. No custom provider was injected (respect caller contract)
|
|
67
|
+
// 2. The workspace actually has pending changes (avoid wasting budget)
|
|
68
|
+
let llmMessage: string | undefined;
|
|
69
|
+
let commitMessageSource: CommitMessageSource = 'deterministic';
|
|
70
|
+
let llmFallbackReason: LLMFallbackReason | undefined;
|
|
71
|
+
|
|
72
|
+
if (!provider) {
|
|
73
|
+
// Guard: skip pre-check if deadline already elapsed to avoid unnecessary mutex contention
|
|
74
|
+
let preClean = false;
|
|
75
|
+
if (!deadlineMs || Date.now() < deadlineMs) {
|
|
76
|
+
try {
|
|
77
|
+
const preStatus = await gitService.getStatus();
|
|
78
|
+
preClean = preStatus.clean;
|
|
79
|
+
} catch {
|
|
80
|
+
// If we can't determine status, assume dirty so we don't skip the commit
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!preClean) {
|
|
85
|
+
try {
|
|
86
|
+
const generator = getCommitMessageGenerator();
|
|
87
|
+
const result = await generator.generateCommitMessage(
|
|
88
|
+
{
|
|
89
|
+
workspaceDir,
|
|
90
|
+
trigger: 'turn',
|
|
91
|
+
sessionId,
|
|
92
|
+
turnNumber,
|
|
93
|
+
changedFiles: [], // File list unavailable outside the git mutex; generator handles empty arrays
|
|
94
|
+
timestampMs: Date.now(),
|
|
95
|
+
},
|
|
96
|
+
{ deadlineMs, changedFiles: [] },
|
|
97
|
+
);
|
|
98
|
+
commitMessageSource = result.source;
|
|
99
|
+
llmFallbackReason = result.reason;
|
|
100
|
+
if (result.source === 'llm') {
|
|
101
|
+
llmMessage = result.message;
|
|
102
|
+
}
|
|
103
|
+
} catch (llmErr) {
|
|
104
|
+
// Never let LLM errors affect the commit path
|
|
105
|
+
log.debug({ err: llmErr }, 'LLM commit message generation failed (non-fatal)');
|
|
106
|
+
llmFallbackReason = 'provider_error';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
63
111
|
const { committed, status } = await gitService.commitIfDirty((st) => {
|
|
64
112
|
const uniqueFiles = [...new Set([...st.staged, ...st.modified, ...st.untracked])];
|
|
65
113
|
|
|
@@ -72,6 +120,10 @@ export async function commitTurnChanges(
|
|
|
72
120
|
timestampMs: Date.now(),
|
|
73
121
|
};
|
|
74
122
|
|
|
123
|
+
// Use LLM message if available, otherwise deterministic
|
|
124
|
+
if (llmMessage) {
|
|
125
|
+
return { message: llmMessage };
|
|
126
|
+
}
|
|
75
127
|
return messageProvider.buildImmediateMessage(ctx);
|
|
76
128
|
}, deadlineMs !== undefined ? { deadlineMs } : undefined);
|
|
77
129
|
|
|
@@ -80,7 +132,14 @@ export async function commitTurnChanges(
|
|
|
80
132
|
if (committed) {
|
|
81
133
|
const uniqueFiles = [...new Set([...status.staged, ...status.modified, ...status.untracked])];
|
|
82
134
|
log.info(
|
|
83
|
-
{
|
|
135
|
+
{
|
|
136
|
+
sessionId,
|
|
137
|
+
turnNumber,
|
|
138
|
+
filesChanged: uniqueFiles.length,
|
|
139
|
+
durationMs: commitDurationMs,
|
|
140
|
+
commitMessageSource,
|
|
141
|
+
...(llmFallbackReason ? { llmFallbackReason } : {}),
|
|
142
|
+
},
|
|
84
143
|
'Turn-boundary commit created',
|
|
85
144
|
);
|
|
86
145
|
|
|
File without changes
|