@skrr-ai/cli 0.1.93 → 0.1.94

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.
Files changed (60) hide show
  1. package/dist/commands/agents/operating-plan.d.ts +11 -0
  2. package/dist/commands/agents/operating-plan.js +54 -0
  3. package/dist/commands/agents/roles/example.d.ts +11 -0
  4. package/dist/commands/agents/roles/example.js +30 -0
  5. package/dist/commands/{commitments/mode.d.ts → agents/roles/export.d.ts} +5 -5
  6. package/dist/commands/agents/roles/export.js +81 -0
  7. package/dist/commands/agents/roles/install.d.ts +12 -0
  8. package/dist/commands/agents/roles/install.js +48 -0
  9. package/dist/commands/agents/roles/list.d.ts +11 -0
  10. package/dist/commands/agents/roles/list.js +36 -0
  11. package/dist/commands/agents/roles/preview.d.ts +14 -0
  12. package/dist/commands/agents/roles/preview.js +49 -0
  13. package/dist/commands/agents/roles/withdraw.d.ts +13 -0
  14. package/dist/commands/agents/roles/withdraw.js +43 -0
  15. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.d.ts +5 -7
  16. package/dist/commands/commitments/{autonomy-requests → cadence-requests}/decide.js +13 -15
  17. package/dist/commands/commitments/cost.d.ts +1 -1
  18. package/dist/commands/commitments/cost.js +1 -1
  19. package/dist/commands/commitments/create.d.ts +0 -7
  20. package/dist/commands/commitments/create.js +9 -98
  21. package/dist/commands/commitments/delivery.js +3 -21
  22. package/dist/commands/commitments/doctor.js +28 -27
  23. package/dist/commands/commitments/effective-policy.js +5 -36
  24. package/dist/commands/commitments/evaluations.d.ts +15 -0
  25. package/dist/commands/commitments/evaluations.js +54 -0
  26. package/dist/commands/commitments/pack/activate.d.ts +11 -0
  27. package/dist/commands/commitments/pack/activate.js +22 -0
  28. package/dist/commands/commitments/pack/bind.js +1 -1
  29. package/dist/commands/commitments/remediate.js +2 -5
  30. package/dist/commands/commitments/show.js +1 -1
  31. package/dist/commands/commitments/update.js +9 -9
  32. package/dist/commands/commitments/wake-capabilities.d.ts +1 -3
  33. package/dist/commands/commitments/wake-capabilities.js +5 -21
  34. package/dist/lib/commitment-product.d.ts +0 -6
  35. package/dist/lib/commitment-product.js +1 -30
  36. package/dist/lib/commitments.d.ts +9 -45
  37. package/dist/lib/commitments.js +24 -95
  38. package/dist/lib/northstars.js +1 -1
  39. package/dist/lib/roles.d.ts +98 -0
  40. package/dist/lib/roles.js +45 -0
  41. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.d.ts +45 -0
  42. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/commitmentExecutionContract.js +550 -0
  43. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.d.ts +7 -44
  44. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/sessionPermissionAuthority.js +8 -47
  45. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.d.ts +45 -0
  46. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/commitmentExecutionContract.js +545 -0
  47. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.d.ts +7 -44
  48. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/sessionPermissionAuthority.js +8 -47
  49. package/dist/node_modules/@skrr-ai/auth-core/package.json +11 -1
  50. package/dist/node_modules/@skrr-ai/data-provider/index.js +7316 -7203
  51. package/dist/node_modules/@skrr-ai/data-provider/package.json +2 -2
  52. package/oclif.manifest.json +22528 -22170
  53. package/package.json +5 -2
  54. package/dist/commands/commitments/autonomy.d.ts +0 -30
  55. package/dist/commands/commitments/autonomy.js +0 -67
  56. package/dist/commands/commitments/mode.js +0 -58
  57. package/dist/commands/commitments/pack/promote.d.ts +0 -19
  58. package/dist/commands/commitments/pack/promote.js +0 -46
  59. package/dist/commands/commitments/pack/shadow.d.ts +0 -20
  60. package/dist/commands/commitments/pack/shadow.js +0 -42
@@ -0,0 +1,550 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.COMMITMENT_EXECUTION_CONTRACT_CAPABILITY = exports.COMMITMENT_EXECUTION_CONTRACT_VERSION = void 0;
4
+ exports.eventRepository = eventRepository;
5
+ exports.normalizeCommitmentExecutionContract = normalizeCommitmentExecutionContract;
6
+ const node_util_1 = require("node:util");
7
+ /** Mode-free responsibility context. Tool permissions belong to the Agent runtime. */
8
+ exports.COMMITMENT_EXECUTION_CONTRACT_VERSION = 3;
9
+ exports.COMMITMENT_EXECUTION_CONTRACT_CAPABILITY = 'commitment_execution_contract_v3';
10
+ const DELIVERY_MODES = new Set([
11
+ 'report_only',
12
+ 'local_patch',
13
+ 'draft_pr',
14
+ 'push_branch',
15
+ 'direct_branch',
16
+ ]);
17
+ const FACT_KEYS = new Set([
18
+ 'id',
19
+ 'source',
20
+ 'type',
21
+ 'eventType',
22
+ 'checkName',
23
+ 'workflowName',
24
+ 'workflowRunId',
25
+ 'revision',
26
+ 'appId',
27
+ 'appSlug',
28
+ 'slug',
29
+ 'app',
30
+ 'action',
31
+ 'status',
32
+ 'conclusion',
33
+ 'sha',
34
+ 'headSha',
35
+ 'head_sha',
36
+ 'branch',
37
+ 'headBranch',
38
+ 'head_branch',
39
+ 'repo',
40
+ 'repository',
41
+ 'fullName',
42
+ 'full_name',
43
+ 'repoFullName',
44
+ 'prNumber',
45
+ 'owner',
46
+ 'name',
47
+ 'number',
48
+ 'checkRunId',
49
+ 'check_run_id',
50
+ 'runId',
51
+ 'run_id',
52
+ 'url',
53
+ 'runUrl',
54
+ 'htmlUrl',
55
+ 'html_url',
56
+ 'createdAt',
57
+ 'updatedAt',
58
+ 'occurredAt',
59
+ 'completed_at',
60
+ 'started_at',
61
+ 'check_run',
62
+ 'check_suite',
63
+ 'workflow_run',
64
+ 'pull_request',
65
+ 'base',
66
+ 'head',
67
+ 'ref',
68
+ 'eventId',
69
+ 'rerun',
70
+ 'checkAnnotations',
71
+ 'path',
72
+ 'startLine',
73
+ 'endLine',
74
+ 'level',
75
+ 'message',
76
+ 'title',
77
+ ]);
78
+ const CAPABILITY_SOURCE_KEYS = new Set([
79
+ 'source',
80
+ 'provider',
81
+ 'capability',
82
+ 'scope',
83
+ 'status',
84
+ 'attestation',
85
+ 'accountFingerprint',
86
+ 'testedAt',
87
+ 'expiresAt',
88
+ 'runtimeId',
89
+ 'reason',
90
+ ]);
91
+ const WORKSPACE_CONSTRAINT_KEYS = new Set([
92
+ 'allowedCommitmentDeliveryModes',
93
+ 'requireAttestedAccessForDeliveryModes',
94
+ 'maxInitiativesPerDay',
95
+ 'maxAutoRunsPerDay',
96
+ 'maxCommitmentActionRunsPerDay',
97
+ 'maxCommitmentObservationRunsPerDay',
98
+ 'maxCommitmentConcurrentRuns',
99
+ 'maxCommitmentCostUsdPerDay',
100
+ 'requireApprovalRiskLevels',
101
+ 'requireApprovalActionClasses',
102
+ 'blockedSourceKinds',
103
+ ]);
104
+ const CAPABILITY_SOURCES = new Set([
105
+ 'event_payload',
106
+ 'cloud_runtime',
107
+ 'local_harness',
108
+ 'platform_grant',
109
+ ]);
110
+ const CAPABILITY_STATUSES = new Set(['available', 'unavailable', 'unknown', 'stale']);
111
+ const CAPABILITY_ATTESTATIONS = new Set(['platform', 'runtime', 'machine', 'none']);
112
+ function plainObject(value) {
113
+ if (!value || typeof value !== 'object' || Array.isArray(value))
114
+ return false;
115
+ const prototype = Object.getPrototypeOf(value);
116
+ return prototype === Object.prototype || prototype === null;
117
+ }
118
+ function hasOnlyKeys(value, allowed) {
119
+ return plainObject(value) && Object.keys(value).every((key) => allowed.has(key));
120
+ }
121
+ function boundedString(value, max = 1000) {
122
+ return typeof value === 'string' && value.length > 0 && value.length <= max;
123
+ }
124
+ function optionalString(value, max = 1000) {
125
+ return typeof value === 'string' && value.length <= max;
126
+ }
127
+ function isoTimestamp(value) {
128
+ return (typeof value === 'string' &&
129
+ value.length <= 40 &&
130
+ Number.isFinite(Date.parse(value)) &&
131
+ new Date(value).toISOString() === value);
132
+ }
133
+ function normalizedEventFactValue(item, depth) {
134
+ if (typeof item === 'string') {
135
+ if (containsCredentialSignature(item))
136
+ return undefined;
137
+ return item.slice(0, 1000);
138
+ }
139
+ if (typeof item === 'number' || typeof item === 'boolean')
140
+ return item;
141
+ if (Array.isArray(item)) {
142
+ return item
143
+ .slice(0, 20)
144
+ .map((entry) => normalizedEventFacts(entry, depth + 1))
145
+ .filter((entry) => entry !== undefined);
146
+ }
147
+ return normalizedEventFacts(item, depth + 1);
148
+ }
149
+ const CREDENTIAL_SIGNATURE_RE = /(?:bearer\s+(?!\[REDACTED\](?:\s|$))\S{8,}|github_pat_[A-Za-z0-9_]{8,}|gh[pousr]_[A-Za-z0-9_]{8,}|xox[baprs]-[A-Za-z0-9-]{8,}|sk_(?:live|test)_[A-Za-z0-9]{8,}|(?:AKIA|ASIA)[A-Z0-9]{16}|eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}|[a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:(?!\[REDACTED\]@)[^\s/@]+@|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
150
+ function containsCredentialSignature(value) {
151
+ return typeof value === 'string' && CREDENTIAL_SIGNATURE_RE.test(value);
152
+ }
153
+ function normalizedEventFacts(value, depth = 0) {
154
+ if (!plainObject(value) || depth > 3)
155
+ return undefined;
156
+ return Object.fromEntries(Object.entries(value)
157
+ .filter(([key]) => FACT_KEYS.has(key))
158
+ .slice(0, 30)
159
+ .map(([key, item]) => [key, normalizedEventFactValue(item, depth)])
160
+ .filter(([, item]) => item !== undefined));
161
+ }
162
+ const CI_LOG_EVIDENCE_KEYS = new Set([
163
+ 'version',
164
+ 'provider',
165
+ 'kind',
166
+ 'repoFullName',
167
+ 'checkRunId',
168
+ 'workflowRunId',
169
+ 'jobId',
170
+ 'capability',
171
+ 'attestation',
172
+ 'fetchedAt',
173
+ 'status',
174
+ 'reason',
175
+ 'byteLength',
176
+ 'lineCount',
177
+ 'excerptLines',
178
+ 'excerpt',
179
+ 'digest',
180
+ // Attribution facts, taken off the job row the server had to fetch to prove
181
+ // identity. Allowed on an UNREADABLE row: attribution happens before the
182
+ // download, so the failing step name survives a log the server could not
183
+ // read. Only text FROM the log is withheld when the read failed.
184
+ 'jobName',
185
+ 'failedStepName',
186
+ 'failedStepNumber',
187
+ ]);
188
+ const CI_LOG_EVIDENCE_STATUSES = new Set(['available', 'truncated', 'unavailable', 'unauthorized']);
189
+ const CI_LOG_EVIDENCE_READABLE = new Set(['available', 'truncated']);
190
+ const CI_LOG_EXCERPT_MAX_CHARS = 12_000;
191
+ function validCiLogEvidence(value) {
192
+ if (!hasOnlyKeys(value, CI_LOG_EVIDENCE_KEYS))
193
+ return false;
194
+ if (value.version !== 1 ||
195
+ value.provider !== 'github' ||
196
+ value.kind !== 'actions_job_log' ||
197
+ value.capability !== 'github:actions:read' ||
198
+ value.attestation !== 'platform' ||
199
+ !CI_LOG_EVIDENCE_STATUSES.has(String(value.status)) ||
200
+ !isoTimestamp(value.fetchedAt) ||
201
+ !/^[\w.-]+\/[\w.-]+$/.test(String(value.repoFullName || '')) ||
202
+ !/^\d{1,20}$/.test(String(value.checkRunId || '')))
203
+ return false;
204
+ for (const key of ['workflowRunId', 'jobId']) {
205
+ if (value[key] !== undefined && !/^\d{1,20}$/.test(String(value[key])))
206
+ return false;
207
+ }
208
+ for (const key of ['byteLength', 'lineCount', 'excerptLines', 'failedStepNumber']) {
209
+ const item = value[key];
210
+ if (item !== undefined && (!Number.isSafeInteger(item) || item < 0))
211
+ return false;
212
+ }
213
+ for (const key of ['jobName', 'failedStepName']) {
214
+ if (value[key] !== undefined && !boundedString(value[key], 200))
215
+ return false;
216
+ }
217
+ if (value.reason !== undefined && !/^[a-z0-9_]{1,80}$/.test(String(value.reason)))
218
+ return false;
219
+ if (value.digest !== undefined && !/^[a-f0-9]{16}$/.test(String(value.digest)))
220
+ return false;
221
+ // A read that failed carries no text: an unreadable log must not be able to
222
+ // arrive looking like a readable one.
223
+ if (!CI_LOG_EVIDENCE_READABLE.has(String(value.status))) {
224
+ return value.excerpt === undefined && value.digest === undefined && Boolean(value.reason);
225
+ }
226
+ return boundedString(value.excerpt, CI_LOG_EXCERPT_MAX_CHARS) && value.jobId !== undefined;
227
+ }
228
+ function validEvidence(value) {
229
+ if (!hasOnlyKeys(value, new Set(['version', 'ci'])))
230
+ return false;
231
+ return (value.version === 1 &&
232
+ Array.isArray(value.ci) &&
233
+ value.ci.length >= 1 &&
234
+ value.ci.length <= 4 &&
235
+ value.ci.every(validCiLogEvidence));
236
+ }
237
+ // Mirror of `validObserve` in
238
+ // api/server/services/Commitments/CommitmentExecutionContract.js. The daemon
239
+ // has no projector, so canonical form is part of validity on both sides:
240
+ // `egress` must be strictly ascending (sorted AND unique). The shared parity
241
+ // fixture holds the two to one verdict.
242
+ const OBSERVE_KEYS = new Set(['egress', 'web', 'shell', 'browser']);
243
+ const OBSERVE_EGRESS_MAX = 32;
244
+ const OBSERVE_HOST_PATTERN = /^(\*\.)?[a-z0-9.-]+$/;
245
+ const OBSERVE_HOST_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
246
+ function evidenceMatchesEvent(row, event) {
247
+ if (!plainObject(row) || !plainObject(event))
248
+ return true;
249
+ const same = (a, b) => String(a).toLowerCase() === String(b).toLowerCase();
250
+ if (event.repoFullName !== undefined && !same(row.repoFullName, event.repoFullName))
251
+ return false;
252
+ if (event.checkRunId !== undefined && !same(row.checkRunId, event.checkRunId))
253
+ return false;
254
+ if (row.workflowRunId !== undefined &&
255
+ event.workflowRunId !== undefined &&
256
+ !same(row.workflowRunId, event.workflowRunId))
257
+ return false;
258
+ return true;
259
+ }
260
+ function validCapabilitySource(value) {
261
+ if (!hasOnlyKeys(value, CAPABILITY_SOURCE_KEYS))
262
+ return false;
263
+ if (!CAPABILITY_SOURCES.has(String(value.source)) ||
264
+ !boundedString(value.provider, 80) ||
265
+ !/^[a-z0-9_.-]{1,80}$/i.test(value.provider) ||
266
+ !boundedString(value.capability, 120) ||
267
+ !/^[a-z0-9_:.-]{1,120}$/i.test(value.capability) ||
268
+ !CAPABILITY_STATUSES.has(String(value.status)) ||
269
+ !CAPABILITY_ATTESTATIONS.has(String(value.attestation)))
270
+ return false;
271
+ if (value.scope !== undefined && !optionalString(value.scope, 500))
272
+ return false;
273
+ if (value.accountFingerprint !== undefined &&
274
+ !/^[a-f0-9]{64}$/i.test(String(value.accountFingerprint)))
275
+ return false;
276
+ if (value.testedAt !== undefined && !isoTimestamp(value.testedAt))
277
+ return false;
278
+ if (value.expiresAt !== undefined && !isoTimestamp(value.expiresAt))
279
+ return false;
280
+ if (value.testedAt &&
281
+ value.expiresAt &&
282
+ Date.parse(String(value.expiresAt)) <= Date.parse(String(value.testedAt)))
283
+ return false;
284
+ if (value.runtimeId !== undefined && !boundedString(value.runtimeId, 200))
285
+ return false;
286
+ if (value.reason !== undefined && !optionalString(value.reason, 1000))
287
+ return false;
288
+ if (value.source === 'event_payload' && value.attestation !== 'platform')
289
+ return false;
290
+ if (value.source === 'platform_grant' && value.attestation !== 'platform')
291
+ return false;
292
+ if (value.source === 'local_harness' &&
293
+ (!['machine', 'none'].includes(String(value.attestation)) ||
294
+ (value.status === 'available' &&
295
+ (value.attestation !== 'machine' ||
296
+ !value.accountFingerprint ||
297
+ !value.testedAt ||
298
+ !value.expiresAt ||
299
+ !value.runtimeId))))
300
+ return false;
301
+ if (value.source === 'cloud_runtime' &&
302
+ (!['runtime', 'none'].includes(String(value.attestation)) ||
303
+ (value.status === 'available' &&
304
+ (value.attestation !== 'runtime' ||
305
+ !value.accountFingerprint ||
306
+ !value.testedAt ||
307
+ !value.expiresAt ||
308
+ !value.runtimeId))))
309
+ return false;
310
+ return true;
311
+ }
312
+ function validStringArray(value, max, values) {
313
+ return (Array.isArray(value) &&
314
+ value.length <= max &&
315
+ value.every((item) => boundedString(item, 120) && (!values || values.has(item))));
316
+ }
317
+ function validWorkspacePolicy(value) {
318
+ if (!hasOnlyKeys(value, new Set(['hardStop', 'hardConstraints'])))
319
+ return false;
320
+ if (value.hardStop !== undefined && value.hardStop !== null) {
321
+ if (!hasOnlyKeys(value.hardStop, new Set(['enabled', 'reason', 'scope'])))
322
+ return false;
323
+ if (typeof value.hardStop.enabled !== 'boolean')
324
+ return false;
325
+ if (value.hardStop.reason !== undefined && !optionalString(value.hardStop.reason, 1000))
326
+ return false;
327
+ if (value.hardStop.scope !== undefined && !optionalString(value.hardStop.scope, 100))
328
+ return false;
329
+ }
330
+ if (value.hardConstraints === undefined)
331
+ return true;
332
+ if (!hasOnlyKeys(value.hardConstraints, WORKSPACE_CONSTRAINT_KEYS))
333
+ return false;
334
+ for (const [key, item] of Object.entries(value.hardConstraints)) {
335
+ if (item === null)
336
+ continue;
337
+ if (key === 'allowedCommitmentDeliveryModes' ||
338
+ key === 'requireAttestedAccessForDeliveryModes') {
339
+ if (!validStringArray(item, 5, DELIVERY_MODES))
340
+ return false;
341
+ }
342
+ else if (key === 'maxCommitmentCostUsdPerDay') {
343
+ if (typeof item !== 'number' || !Number.isFinite(item) || item < 0)
344
+ return false;
345
+ }
346
+ else if (key.startsWith('max')) {
347
+ if (!Number.isSafeInteger(item) || Number(item) < 0)
348
+ return false;
349
+ }
350
+ else if (key === 'requireApprovalRiskLevels') {
351
+ if (!validStringArray(item, 3, new Set(['low', 'medium', 'high'])))
352
+ return false;
353
+ }
354
+ else if (!validStringArray(item, key === 'blockedSourceKinds' ? 50 : 20))
355
+ return false;
356
+ }
357
+ return true;
358
+ }
359
+ function validBranch(value) {
360
+ return (boundedString(value, 255) &&
361
+ value !== 'HEAD' &&
362
+ !value.startsWith('refs/') &&
363
+ !value.startsWith('origin/') &&
364
+ !value.startsWith('-') &&
365
+ !value.startsWith('/') &&
366
+ !value.endsWith('/') &&
367
+ !value.endsWith('.') &&
368
+ // Control characters are exactly what this rejects: a Git ref name may not
369
+ // contain them, so the class is deliberate rather than an oversight.
370
+ // eslint-disable-next-line no-control-regex
371
+ !/[\s~^:?*[\\\x00-\x1f\x7f]/.test(value) &&
372
+ !value.includes('..') &&
373
+ !value.includes('@{') &&
374
+ !value.includes('//') &&
375
+ value !== '@' &&
376
+ !value.split('/').some((part) => part.startsWith('.') || part.endsWith('.lock')));
377
+ }
378
+ function validDelivery(value) {
379
+ if (!hasOnlyKeys(value, new Set(['version', 'mode', 'repo', 'baseBranch', 'expectations'])))
380
+ return false;
381
+ if (value.version !== 1 || !DELIVERY_MODES.has(String(value.mode)))
382
+ return false;
383
+ if (value.repo !== undefined &&
384
+ (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(String(value.repo)) ||
385
+ String(value.repo) !== String(value.repo).toLowerCase()))
386
+ return false;
387
+ if (value.baseBranch !== undefined && !validBranch(value.baseBranch))
388
+ return false;
389
+ if (['draft_pr', 'push_branch', 'direct_branch'].includes(String(value.mode)) &&
390
+ (!value.repo || !value.baseBranch))
391
+ return false;
392
+ if (value.expectations !== undefined) {
393
+ if (!Array.isArray(value.expectations) || value.expectations.length > 20)
394
+ return false;
395
+ const ids = new Set();
396
+ const allowed = new Set([
397
+ 'id',
398
+ 'shape',
399
+ 'kind',
400
+ 'format',
401
+ 'target',
402
+ 'minimumAuthority',
403
+ 'description',
404
+ 'presetKey',
405
+ ]);
406
+ for (const expectation of value.expectations) {
407
+ if (!hasOnlyKeys(expectation, allowed) ||
408
+ !['code', 'page', 'url', 'file', 'message', 'dataset'].includes(String(expectation.kind)))
409
+ return false;
410
+ if (expectation.id !== undefined) {
411
+ if (!boundedString(expectation.id, 64) ||
412
+ expectation.id === 'commitment-delivery' ||
413
+ ids.has(expectation.id))
414
+ return false;
415
+ ids.add(expectation.id);
416
+ }
417
+ if (expectation.shape !== undefined &&
418
+ !['checkable', 'judged'].includes(String(expectation.shape)))
419
+ return false;
420
+ if (expectation.minimumAuthority !== undefined &&
421
+ !['self_reported', 'human_confirmed', 'observation_backed'].includes(String(expectation.minimumAuthority)))
422
+ return false;
423
+ for (const [key, max] of [
424
+ ['format', 40],
425
+ ['target', 500],
426
+ ['description', 500],
427
+ ['presetKey', 64],
428
+ ]) {
429
+ if (expectation[key] !== undefined && !optionalString(expectation[key], max))
430
+ return false;
431
+ }
432
+ }
433
+ }
434
+ return true;
435
+ }
436
+ function validDeliverySource(value) {
437
+ if (!hasOnlyKeys(value, new Set(['parentTaskId', 'branch', 'headSha', 'baseCommit', 'runtime'])))
438
+ return false;
439
+ return (typeof value.parentTaskId === 'string' &&
440
+ value.parentTaskId.length > 0 &&
441
+ value.parentTaskId.length <= 200 &&
442
+ typeof value.branch === 'string' &&
443
+ /^[A-Za-z0-9][A-Za-z0-9_./-]{0,239}$/.test(value.branch) &&
444
+ !value.branch.includes('..') &&
445
+ !value.branch.includes('@{') &&
446
+ /^[a-f0-9]{40,64}$/i.test(value.headSha) &&
447
+ typeof value.baseCommit === 'string' &&
448
+ /^[a-f0-9]{40,64}$/i.test(value.baseCommit) &&
449
+ hasOnlyKeys(value.runtime, new Set(['kind', 'id'])) &&
450
+ ['local_daemon', 'hosted_machine'].includes(String(value.runtime.kind)) &&
451
+ boundedString(value.runtime.id, 200) &&
452
+ (value.runtime.kind !== 'hosted_machine' || value.runtime.id.startsWith('sbs_')));
453
+ }
454
+ function eventSourceRevision(event) {
455
+ if (!event)
456
+ return '';
457
+ const nestedCheck = event.check_run;
458
+ const nestedWorkflow = event.workflow_run;
459
+ const value = event.headSha ||
460
+ event.head_sha ||
461
+ event.sha ||
462
+ nestedCheck?.head_sha ||
463
+ nestedWorkflow?.head_sha;
464
+ return /^[a-f0-9]{40,64}$/i.test(String(value || '')) ? String(value).toLowerCase() : '';
465
+ }
466
+ function eventRepository(event) {
467
+ if (!event)
468
+ return '';
469
+ const repository = event.repository;
470
+ const value = event.repoFullName || repository?.full_name || repository?.fullName;
471
+ return /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(String(value || ''))
472
+ ? String(value).toLowerCase()
473
+ : '';
474
+ }
475
+ function failedGitHubCheck(event) {
476
+ const eventType = String(event?.eventType || event?.type || '').toLowerCase();
477
+ return eventType === 'github.check_run_failed' || eventType === 'check_run_failed';
478
+ }
479
+ /** One strict parser used at storage admission and at runtime admission. */
480
+ function normalizeCommitmentExecutionContract(value) {
481
+ if (!hasOnlyKeys(value, new Set([
482
+ 'version',
483
+ 'kind',
484
+ 'responsibilityRef',
485
+ 'responsibility',
486
+ 'event',
487
+ 'workspace',
488
+ 'delivery',
489
+ 'capabilitySources',
490
+ 'evidence',
491
+ 'workspacePolicy',
492
+ 'deliverySource',
493
+ 'mustNot',
494
+ 'return',
495
+ ])))
496
+ return null;
497
+ const contract = value;
498
+ const event = contract.event === undefined ? null : contract.event;
499
+ const eventSha = eventSourceRevision(event);
500
+ const eventRepo = eventRepository(event);
501
+ const taskDelivery = contract.kind === 'task_delivery';
502
+ if (contract.version !== exports.COMMITMENT_EXECUTION_CONTRACT_VERSION ||
503
+ !['commitment', 'task_delivery'].includes(contract.kind) ||
504
+ !hasOnlyKeys(contract.responsibilityRef, new Set(['kind', 'id', 'version'])) ||
505
+ contract.responsibilityRef.kind !== (taskDelivery ? 'task' : 'commitment') ||
506
+ !boundedString(contract.responsibilityRef.id, 240) ||
507
+ !Number.isSafeInteger(contract.responsibilityRef.version) ||
508
+ contract.responsibilityRef.version < 1 ||
509
+ !boundedString(contract.responsibility, 2000) ||
510
+ !hasOnlyKeys(contract.workspace, new Set(['kind', 'base', 'fetchBase'])) ||
511
+ contract.workspace.kind !== 'ephemeral' ||
512
+ !boundedString(contract.workspace.base, 255) ||
513
+ (contract.workspace.fetchBase !== undefined &&
514
+ (contract.workspace.fetchBase !== true || contract.workspace.kind !== 'ephemeral')) ||
515
+ !validDelivery(contract.delivery) ||
516
+ !Array.isArray(contract.capabilitySources) ||
517
+ contract.capabilitySources.length > 32 ||
518
+ !contract.capabilitySources.every(validCapabilitySource) ||
519
+ !validWorkspacePolicy(contract.workspacePolicy ?? {}) ||
520
+ !(0, node_util_1.isDeepStrictEqual)(contract.mustNot, [
521
+ 'change_workspace_administration',
522
+ 'broaden_credentials',
523
+ ...(taskDelivery ? ['reimplement_source_work'] : []),
524
+ ]) ||
525
+ (taskDelivery
526
+ ? contract.return !== 'task_output'
527
+ : contract.return !== 'commitment_result_v1') ||
528
+ (event !== null &&
529
+ (!plainObject(event) || !(0, node_util_1.isDeepStrictEqual)(event, normalizedEventFacts(event)))) ||
530
+ (eventSha && contract.workspace.base.toLowerCase() !== eventSha) ||
531
+ (eventRepo &&
532
+ contract.delivery.repo &&
533
+ String(contract.delivery.repo).toLowerCase() !== eventRepo) ||
534
+ (failedGitHubCheck(event) && (!eventSha || !eventRepo)) ||
535
+ (contract.evidence !== undefined &&
536
+ (taskDelivery ||
537
+ !validEvidence(contract.evidence) ||
538
+ !contract.evidence.ci.every((row) => evidenceMatchesEvent(row, event)))) ||
539
+ (taskDelivery &&
540
+ (event !== null ||
541
+ !['draft_pr', 'push_branch', 'direct_branch'].includes(contract.delivery.mode) ||
542
+ contract.workspace.kind !== 'ephemeral' ||
543
+ !validDeliverySource(contract.deliverySource) ||
544
+ contract.responsibilityRef.id !== contract.deliverySource?.parentTaskId ||
545
+ contract.workspace.base.toLowerCase() !==
546
+ String(contract.deliverySource?.baseCommit).toLowerCase())) ||
547
+ (!taskDelivery && contract.deliverySource !== undefined))
548
+ return null;
549
+ return JSON.parse(JSON.stringify(contract));
550
+ }
@@ -1,33 +1,5 @@
1
- /**
2
- * Session permission authority — the ONE rule that decides whether a session's
3
- * execution contract governs the run's permissions, shared by the server and
4
- * the daemon (OSK-11975 introduced it; it was implemented twice — the server
5
- * read `kind !== 'commitment'`, the daemon read the `permissionSource` marker —
6
- * which is exactly how a new contract kind or a new source value would drift
7
- * them apart).
8
- *
9
- * The wire model:
10
- *
11
- * - The contract is closed (`hasOnlyKeys` on the daemon), so "whose
12
- * permissions govern" can never ride INSIDE it. It is a top-level session
13
- * field: `permissionSource`.
14
- * - `'agent'` means the Agent's own configured permission mode, tool policy,
15
- * Grants, session env, MCP servers and machine consent govern the run,
16
- * exactly as for an unattended Task of the same Agent. Today only a
17
- * `commitment` contract may carry it.
18
- * - A `commitment` contract WITHOUT the marker (an older server mid-deploy)
19
- * still governs permissions — the marker is the version signal, so its
20
- * absence must keep the legacy fence, not silently lift it.
21
- * - `task_delivery` contracts always govern, marker or not.
22
- *
23
- * Server vs daemon asymmetry, on purpose: the server STAMPS the marker
24
- * (`permissionSourceForContract`), so on its own side the predicate reduces to
25
- * "a commitment never governs" — `contractGovernsPermissions(c,
26
- * permissionSourceForContract(c))` is exactly `kind !== 'commitment'`. The
27
- * daemon reads the marker it received, because it cannot distinguish "new
28
- * server, agent permissions" from "old server, contract fence" any other way.
29
- * Both call the same function; only the input differs.
30
- */
1
+ /** Commitment context never overrides Agent permissions. Version 3 admission
2
+ * rejects old contracts; typed Task delivery is a separate frozen operation. */
31
3
  /** The wire vocabulary for `permissionSource`. Top-level session field. */
32
4
  export declare const SESSION_PERMISSION_SOURCES: readonly ["agent"];
33
5
  export type SessionPermissionSource = (typeof SESSION_PERMISSION_SOURCES)[number];
@@ -37,7 +9,7 @@ export type SessionPermissionSource = (typeof SESSION_PERMISSION_SOURCES)[number
37
9
  * a daemon without it (`COMMITMENT_AGENT_PERMISSIONS_RUNTIME_UNSUPPORTED`), so
38
10
  * the marker never reaches a daemon that would ignore it.
39
11
  */
40
- export declare const COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = "commitment_execution_contract_v2_agent_permissions";
12
+ export declare const COMMITMENT_AGENT_PERMISSIONS_CAPABILITY = "commitment_execution_contract_v3";
41
13
  /**
42
14
  * The minimal contract shape this rule reads. The predicate only ever needs
43
15
  * `kind` — keeping the input structural means this module never imports the
@@ -50,8 +22,7 @@ export type PermissionContractKind = {
50
22
  } | null | undefined;
51
23
  /**
52
24
  * Untrusted wire input: anything but an exact vocabulary literal reads as
53
- * absent. Never widen by coercion — an unrecognized future value must degrade
54
- * to "contract governs", the conservative answer.
25
+ * absent. An unknown marker carries no authority. Contract admission is validated separately.
55
26
  */
56
27
  export declare function normalizeSessionPermissionSource(value: unknown): SessionPermissionSource | undefined;
57
28
  /**
@@ -60,18 +31,10 @@ export declare function normalizeSessionPermissionSource(value: unknown): Sessio
60
31
  * kind keeps its own authority and stamps nothing.
61
32
  */
62
33
  export declare function permissionSourceForContract(contract: PermissionContractKind): SessionPermissionSource | undefined;
63
- /**
64
- * Whether the contract is the run's PERMISSION authority — the single place
65
- * this rule lives.
66
- *
67
- * True for a non-commitment contract regardless of marker, and for a
68
- * commitment whose server did not declare agent permissions. False for no
69
- * contract at all, and for a commitment carrying `permissionSource: 'agent'`,
70
- * which then follows the Agent's own permissions exactly as a Task does while
71
- * the contract keeps its non-permission roles (normalized shape, ephemeral
72
- * worktree, result validation, `GIT_OPTIONAL_LOCKS`, provider restriction).
34
+ /** Only typed Task delivery owns an effect-specific permission boundary.
35
+ * A Commitment is context for the Agent and cannot grant or restrict tools.
73
36
  */
74
- export declare function contractGovernsPermissions(contract: PermissionContractKind, permissionSource: unknown): boolean;
37
+ export declare function contractGovernsPermissions(contract: PermissionContractKind, _permissionSource?: unknown): boolean;
75
38
  /**
76
39
  * The contract when it governs permissions, else `undefined` — for the sites
77
40
  * that used `executionContract` as "is this run permission-fenced".