vellum 0.2.7 → 0.2.8
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/bun.lock +2 -2
- package/package.json +2 -2
- package/src/__tests__/asset-materialize-tool.test.ts +2 -2
- package/src/__tests__/checker.test.ts +104 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +458 -0
- package/src/__tests__/ipc-snapshot.test.ts +11 -0
- package/src/__tests__/oauth-callback-registry.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +298 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +51 -12
- package/src/__tests__/public-ingress-urls.test.ts +206 -0
- package/src/__tests__/tool-executor.test.ts +88 -0
- package/src/__tests__/turn-commit.test.ts +64 -0
- package/src/calls/twilio-config.ts +17 -1
- package/src/calls/twilio-routes.ts +10 -2
- package/src/calls/twilio-webhook-urls.ts +18 -21
- package/src/config/defaults.ts +4 -0
- package/src/config/schema.ts +30 -2
- package/src/config/system-prompt.ts +1 -1
- package/src/config/types.ts +1 -0
- package/src/daemon/computer-use-session.ts +2 -1
- package/src/daemon/handlers/config.ts +51 -2
- package/src/daemon/handlers/sessions.ts +2 -2
- package/src/daemon/handlers/work-items.ts +1 -1
- package/src/daemon/ipc-contract-inventory.json +4 -0
- package/src/daemon/ipc-contract.ts +16 -1
- package/src/daemon/session-tool-setup.ts +7 -0
- package/src/inbound/public-ingress-urls.ts +106 -0
- package/src/memory/attachments-store.ts +0 -1
- package/src/memory/channel-delivery-store.ts +0 -1
- package/src/memory/conversation-key-store.ts +0 -1
- package/src/memory/db.ts +346 -149
- package/src/memory/runs-store.ts +0 -3
- package/src/memory/schema.ts +0 -4
- package/src/runtime/http-server.ts +84 -2
- package/src/security/oauth-callback-registry.ts +56 -0
- package/src/security/oauth2.ts +174 -58
- package/src/swarm/backend-claude-code.ts +1 -1
- package/src/tools/assets/search.ts +1 -36
- package/src/tools/claude-code/claude-code.ts +3 -3
- package/src/tools/tasks/work-item-list.ts +16 -2
- package/src/workspace/provider-commit-message-generator.ts +39 -23
- package/src/workspace/turn-commit.ts +6 -2
|
@@ -10,9 +10,10 @@ export type CommitMessageSource = 'llm' | 'deterministic';
|
|
|
10
10
|
export type LLMFallbackReason =
|
|
11
11
|
| 'disabled'
|
|
12
12
|
| 'missing_provider_api_key'
|
|
13
|
-
| 'provider_not_initialized'
|
|
14
13
|
| 'breaker_open'
|
|
15
14
|
| 'insufficient_budget'
|
|
15
|
+
| 'missing_fast_model'
|
|
16
|
+
| 'provider_not_initialized'
|
|
16
17
|
| 'timeout'
|
|
17
18
|
| 'provider_error'
|
|
18
19
|
| 'invalid_output';
|
|
@@ -103,17 +104,25 @@ export class ProviderCommitMessageGenerator {
|
|
|
103
104
|
const config = getConfig();
|
|
104
105
|
const llmConfig = config.workspaceGit.commitMessageLLM;
|
|
105
106
|
|
|
107
|
+
// ── Fallback check order (canonical) ──────────────────────────────
|
|
108
|
+
// 1. disabled
|
|
109
|
+
// 2. missing_provider_api_key (except keyless providers like ollama)
|
|
110
|
+
// 3. breaker_open
|
|
111
|
+
// 4. insufficient_budget
|
|
112
|
+
// 5. missing_fast_model
|
|
113
|
+
// 6. provider_not_initialized
|
|
114
|
+
// 7. call provider → timeout / provider_error / invalid_output
|
|
115
|
+
// ──────────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
106
117
|
// Step 1: Feature gate
|
|
107
118
|
if (!llmConfig.enabled) {
|
|
108
119
|
return buildDeterministicResult(context, 'disabled');
|
|
109
120
|
}
|
|
110
|
-
|
|
111
|
-
// Step 2: Provider gate
|
|
112
121
|
if (!llmConfig.useConfiguredProvider) {
|
|
113
122
|
return buildDeterministicResult(context, 'disabled');
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
// Step 2
|
|
125
|
+
// Step 2: API key preflight (skip for providers that run without a key)
|
|
117
126
|
if (!KEYLESS_PROVIDERS.has(config.provider)) {
|
|
118
127
|
const providerApiKey = config.apiKeys[config.provider];
|
|
119
128
|
if (!providerApiKey || providerApiKey === '') {
|
|
@@ -143,7 +152,19 @@ export class ProviderCommitMessageGenerator {
|
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
|
|
146
|
-
// Step 5:
|
|
155
|
+
// Step 5: Fast model preflight — resolve before any provider call
|
|
156
|
+
const fastModel = llmConfig.providerFastModelOverrides[config.provider]
|
|
157
|
+
?? PROVIDER_DEFAULT_FAST_MODELS[config.provider];
|
|
158
|
+
|
|
159
|
+
if (!fastModel) {
|
|
160
|
+
log.debug(
|
|
161
|
+
{ provider: config.provider },
|
|
162
|
+
'No fast model resolvable for provider; falling back to deterministic',
|
|
163
|
+
);
|
|
164
|
+
return buildDeterministicResult(context, 'missing_fast_model');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Step 6 + 7: Call the provider
|
|
147
168
|
try {
|
|
148
169
|
const { getProvider } = await import('../providers/registry.js');
|
|
149
170
|
|
|
@@ -179,14 +200,6 @@ export class ProviderCommitMessageGenerator {
|
|
|
179
200
|
},
|
|
180
201
|
];
|
|
181
202
|
|
|
182
|
-
// Resolve fast model
|
|
183
|
-
const fastModel = llmConfig.providerFastModelOverrides[config.provider]
|
|
184
|
-
?? PROVIDER_DEFAULT_FAST_MODELS[config.provider];
|
|
185
|
-
if (!fastModel) {
|
|
186
|
-
log.debug({ provider: config.provider }, 'No default fast model for provider; falling back to deterministic');
|
|
187
|
-
return buildDeterministicResult(context, 'provider_error');
|
|
188
|
-
}
|
|
189
|
-
|
|
190
203
|
// AbortController with timeout
|
|
191
204
|
const ac = new AbortController();
|
|
192
205
|
const timer = setTimeout(() => ac.abort(), llmConfig.timeoutMs);
|
|
@@ -199,7 +212,11 @@ export class ProviderCommitMessageGenerator {
|
|
|
199
212
|
SYSTEM_PROMPT,
|
|
200
213
|
{
|
|
201
214
|
signal: ac.signal,
|
|
202
|
-
config: {
|
|
215
|
+
config: {
|
|
216
|
+
model: fastModel,
|
|
217
|
+
max_tokens: llmConfig.maxTokens,
|
|
218
|
+
temperature: llmConfig.temperature,
|
|
219
|
+
},
|
|
203
220
|
},
|
|
204
221
|
);
|
|
205
222
|
} catch (err: unknown) {
|
|
@@ -230,21 +247,20 @@ export class ProviderCommitMessageGenerator {
|
|
|
230
247
|
return buildDeterministicResult(context, 'invalid_output');
|
|
231
248
|
}
|
|
232
249
|
|
|
233
|
-
//
|
|
234
|
-
const
|
|
235
|
-
if (
|
|
250
|
+
// Cap subject line to 72 chars deterministically (no fallback, no breaker failure)
|
|
251
|
+
const lines = text.split('\n');
|
|
252
|
+
if (lines[0].length > 72) {
|
|
236
253
|
log.debug(
|
|
237
|
-
{
|
|
238
|
-
'LLM subject line
|
|
254
|
+
{ originalLength: lines[0].length },
|
|
255
|
+
'Capping LLM subject line to 72 chars',
|
|
239
256
|
);
|
|
240
|
-
|
|
241
|
-
return buildDeterministicResult(context, 'invalid_output');
|
|
257
|
+
lines[0] = lines[0].slice(0, 72);
|
|
242
258
|
}
|
|
259
|
+
const finalMessage = lines.join('\n');
|
|
243
260
|
|
|
244
261
|
this.recordSuccess();
|
|
245
|
-
return { message:
|
|
262
|
+
return { message: finalMessage, source: 'llm' };
|
|
246
263
|
} catch (err: unknown) {
|
|
247
|
-
// Step 6: Any error -> deterministic fallback
|
|
248
264
|
log.warn(
|
|
249
265
|
{ err: err instanceof Error ? err.message : String(err) },
|
|
250
266
|
'Commit message LLM provider error; falling back to deterministic',
|
|
@@ -72,10 +72,14 @@ export async function commitTurnChanges(
|
|
|
72
72
|
if (!provider) {
|
|
73
73
|
// Guard: skip pre-check if deadline already elapsed to avoid unnecessary mutex contention
|
|
74
74
|
let preClean = false;
|
|
75
|
+
let candidateChangedFiles: string[] = [];
|
|
75
76
|
if (!deadlineMs || Date.now() < deadlineMs) {
|
|
76
77
|
try {
|
|
77
78
|
const preStatus = await gitService.getStatus();
|
|
78
79
|
preClean = preStatus.clean;
|
|
80
|
+
if (!preClean) {
|
|
81
|
+
candidateChangedFiles = [...new Set([...preStatus.staged, ...preStatus.modified, ...preStatus.untracked])];
|
|
82
|
+
}
|
|
79
83
|
} catch {
|
|
80
84
|
// If we can't determine status, assume dirty so we don't skip the commit
|
|
81
85
|
}
|
|
@@ -90,10 +94,10 @@ export async function commitTurnChanges(
|
|
|
90
94
|
trigger: 'turn',
|
|
91
95
|
sessionId,
|
|
92
96
|
turnNumber,
|
|
93
|
-
changedFiles:
|
|
97
|
+
changedFiles: candidateChangedFiles,
|
|
94
98
|
timestampMs: Date.now(),
|
|
95
99
|
},
|
|
96
|
-
{ deadlineMs, changedFiles:
|
|
100
|
+
{ deadlineMs, changedFiles: candidateChangedFiles },
|
|
97
101
|
);
|
|
98
102
|
commitMessageSource = result.source;
|
|
99
103
|
llmFallbackReason = result.reason;
|