@dsh-enhanced/assistant-web-owner 0.1.30

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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +226 -0
  3. package/bin/dsh-autonomy-doctor.js +7 -0
  4. package/bin/dsh-web-owner-setup.js +7 -0
  5. package/cordis.patch.yml +6 -0
  6. package/lib/THIRD_PARTY_LICENSES +23 -0
  7. package/lib/autonomy.d.ts +22 -0
  8. package/lib/autonomy.d.ts.map +1 -0
  9. package/lib/autonomy.js +267 -0
  10. package/lib/autonomy.js.map +1 -0
  11. package/lib/client.d.ts +1 -0
  12. package/lib/client.d.ts.map +1 -0
  13. package/lib/client.js +17418 -0
  14. package/lib/client.js.map +1 -0
  15. package/lib/doctor.d.ts +22 -0
  16. package/lib/doctor.d.ts.map +1 -0
  17. package/lib/doctor.js +159 -0
  18. package/lib/doctor.js.map +1 -0
  19. package/lib/goal-admission.d.ts +94 -0
  20. package/lib/goal-admission.d.ts.map +1 -0
  21. package/lib/goal-admission.js +553 -0
  22. package/lib/goal-admission.js.map +1 -0
  23. package/lib/goal-setup.d.ts +15 -0
  24. package/lib/goal-setup.d.ts.map +1 -0
  25. package/lib/goal-setup.js +145 -0
  26. package/lib/goal-setup.js.map +1 -0
  27. package/lib/index.d.ts +42 -0
  28. package/lib/index.d.ts.map +1 -0
  29. package/lib/index.js +200 -0
  30. package/lib/index.js.map +1 -0
  31. package/lib/notices.d.ts +11 -0
  32. package/lib/notices.d.ts.map +1 -0
  33. package/lib/notices.js +13 -0
  34. package/lib/notices.js.map +1 -0
  35. package/lib/repair-setup.d.ts +50 -0
  36. package/lib/repair-setup.d.ts.map +1 -0
  37. package/lib/repair-setup.js +165 -0
  38. package/lib/repair-setup.js.map +1 -0
  39. package/lib/setup.d.ts +24 -0
  40. package/lib/setup.d.ts.map +1 -0
  41. package/lib/setup.js +302 -0
  42. package/lib/setup.js.map +1 -0
  43. package/lib/typert.d.ts +83 -0
  44. package/lib/typert.d.ts.map +1 -0
  45. package/lib/typert.js +37 -0
  46. package/lib/typert.js.map +1 -0
  47. package/lib/version.d.ts +2 -0
  48. package/lib/version.d.ts.map +1 -0
  49. package/lib/version.js +2 -0
  50. package/lib/version.js.map +1 -0
  51. package/package.json +181 -0
@@ -0,0 +1,553 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { isAbsolute, join, normalize } from 'node:path';
3
+ import { isDeepStrictEqual } from 'node:util';
4
+ import { isMap, isScalar, isSeq, parseDocument } from 'yaml';
5
+ import { Config as GoalsConfig, validateGoalStrategyConfig } from '@dsh-enhanced/assistant-goals';
6
+ import { compileAcceptanceProfiles, createVerifierAuthorities } from '@dsh-enhanced/assistant-verifier';
7
+ import { DEEPSEEK_CHAT_COMPLETIONS_CONTRACT, DEEPSEEK_MODELS, DEEPSEEK_PROVIDER } from '@dsh-enhanced/assistant-deepseek-budget';
8
+ import { inspectAutonomyProfile } from './doctor.js';
9
+ import * as Actions from '@dsh-enhanced/assistant-actions';
10
+ import { literalPath } from './setup.js';
11
+ function fail(reason) { throw new Error(`goal setup: ${reason}`); }
12
+ function shape(value, required, optional = []) {
13
+ if (value === null || typeof value !== 'object' || Array.isArray(value)
14
+ || required.some(key => !Object.hasOwn(value, key)) || Object.keys(value).some(key => !required.includes(key) && !optional.includes(key)))
15
+ fail('invalid task fields');
16
+ }
17
+ function integer(value, min, max) {
18
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < min || value > max)
19
+ fail('invalid task limit');
20
+ }
21
+ function route(value) {
22
+ shape(value, ['provider', 'model']);
23
+ for (const field of ['provider', 'model']) {
24
+ const item = value[field];
25
+ if (typeof item !== 'string' || item.length === 0 || item.length > 200 || item.trim() !== item || /[\p{Cc}]/u.test(item))
26
+ fail('invalid model route');
27
+ }
28
+ }
29
+ /** Read only the public, effective default-route fields from DSH's user settings layer. */
30
+ export function parseSettingsDefaultModelRoute(source) {
31
+ if (Buffer.byteLength(source, 'utf8') > 1024 * 1024)
32
+ fail('settings.yaml exceeds 1 MiB');
33
+ const document = parseDocument(source);
34
+ if (document.errors.length > 0 || !isMap(document.contents) || document.contents.tag !== undefined)
35
+ fail('settings.yaml must be an untagged YAML mapping');
36
+ const selected = document.contents.get('agent-default-model', true);
37
+ if (selected === undefined)
38
+ return undefined;
39
+ if (!isMap(selected) || selected.tag !== undefined)
40
+ fail('settings.agent-default-model must be an untagged YAML mapping');
41
+ const fields = {};
42
+ for (const key of ['provider', 'model']) {
43
+ const value = selected.get(key, true);
44
+ if (!isScalar(value) || value.tag !== undefined || typeof value.value !== 'string')
45
+ fail('settings.agent-default-model must contain public provider and model strings');
46
+ fields[key] = value.value;
47
+ }
48
+ route(fields);
49
+ return fields;
50
+ }
51
+ /** Parse bounded local operator data. No tagged YAML, scripts, credentials, or arbitrary model routes. */
52
+ export function parseGoalAdmissionTask(source) {
53
+ if (Buffer.byteLength(source, 'utf8') > 1024 * 1024)
54
+ fail('task file exceeds 1 MiB');
55
+ let input;
56
+ try {
57
+ input = JSON.parse(source);
58
+ }
59
+ catch {
60
+ fail('task file must be JSON');
61
+ }
62
+ const version = input !== null && typeof input === 'object' && !Array.isArray(input) ? input.version : undefined;
63
+ if (version === 1)
64
+ shape(input, ['version', 'objective', 'model', 'maxGoalRounds', 'stepMaxDurationMs', 'executionBudget', 'verification'], ['apiKeyEnv', 'wake', 'strategy']);
65
+ else if (version === 2)
66
+ shape(input, ['version', 'objective', 'route', 'maxGoalRounds', 'stepMaxDurationMs', 'executionBudget', 'verification'], ['wake', 'strategy', 'repositoryDelivery']);
67
+ else
68
+ fail('unsupported task version');
69
+ if (typeof input.objective !== 'string' || input.objective.length === 0 || input.objective.trim() !== input.objective
70
+ || Buffer.byteLength(input.objective) > 8192 || /[\p{Cc}]/u.test(input.objective))
71
+ fail('invalid objective');
72
+ if (input.version === 1 && (!DEEPSEEK_MODELS.includes(input.model)
73
+ || input.apiKeyEnv !== undefined && (typeof input.apiKeyEnv !== 'string' || !/^[A-Z_][A-Z0-9_]{0,127}$/u.test(input.apiKeyEnv))))
74
+ fail('invalid DeepSeek model or credential reference');
75
+ if (input.version === 2)
76
+ route(input.route);
77
+ integer(input.maxGoalRounds, 1, 32);
78
+ integer(input.stepMaxDurationMs, 1000, 300000);
79
+ if (input.strategy !== undefined)
80
+ input.strategy = validateGoalStrategyConfig(input.strategy);
81
+ if (input.version === 1)
82
+ shape(input.executionBudget, ['modelCalls', 'toolCalls', 'inputTokens', 'outputTokens', 'durationMs', 'maxOutputTokensPerCall']);
83
+ else
84
+ shape(input.executionBudget, ['mode', 'modelCalls', 'toolCalls', 'durationMs', 'maxOutputTokensPerCall', 'routes']);
85
+ const budget = input.executionBudget;
86
+ for (const name of input.version === 1 ? ['modelCalls', 'toolCalls', 'inputTokens', 'outputTokens'] : ['modelCalls', 'toolCalls'])
87
+ integer(budget[name], 1, 1_000_000_000);
88
+ integer(budget.durationMs, 1, 31 * 86_400_000);
89
+ integer(budget.maxOutputTokensPerCall, 1, 32768);
90
+ if (input.version === 1 && (budget.inputTokens < 2_097_152 || budget.outputTokens < budget.maxOutputTokensPerCall))
91
+ fail('budget cannot admit the fixed model route');
92
+ if (input.version === 2) {
93
+ if (budget.mode !== 'calls' || !Array.isArray(budget.routes) || budget.routes.length !== 1)
94
+ fail('v2 calls budget needs one exact route');
95
+ route(budget.routes[0]);
96
+ if (!isDeepStrictEqual(budget.routes[0], input.route))
97
+ fail('budget route must equal task route');
98
+ }
99
+ shape(input.verification, ['artifactPath', 'command', 'cases', 'maxRuns', 'maxTotalDurationMs', 'maxDurationMs', 'maxOutputBytes']);
100
+ const verification = input.verification;
101
+ for (const name of ['artifactPath', 'command'])
102
+ if (typeof verification[name] !== 'string' || verification[name].length === 0)
103
+ fail('invalid verification input');
104
+ if (!Array.isArray(verification.cases) || verification.cases.length === 0 || verification.cases.length > 32)
105
+ fail('invalid verification cases');
106
+ for (const value of verification.cases) {
107
+ shape(value, ['stdin', 'expectedStdout', 'expectedExitCode']);
108
+ if (typeof value.stdin !== 'string' || typeof value.expectedStdout !== 'string')
109
+ fail('invalid verification case');
110
+ integer(value.expectedExitCode, 0, 255);
111
+ }
112
+ integer(verification.maxRuns, 1, 10000);
113
+ integer(verification.maxDurationMs, 1000, 300000);
114
+ integer(verification.maxTotalDurationMs, verification.maxDurationMs, 86_400_000);
115
+ integer(verification.maxOutputBytes, 1, 1024 * 1024);
116
+ const verificationWindow = verification.maxDurationMs * verification.cases.length;
117
+ if (verificationWindow >= input.stepMaxDurationMs)
118
+ fail('verification needs time within the step deadline');
119
+ if (budget.durationMs <= input.stepMaxDurationMs + 2 * verificationWindow)
120
+ fail('execution budget cannot cover the configured native round and verification');
121
+ if (input.wake !== undefined) {
122
+ shape(input.wake, ['maxDelayMs', 'runTimeoutMs', 'maxRuns']);
123
+ integer(input.wake.maxDelayMs, 1, budget.durationMs - 1);
124
+ integer(input.wake.runTimeoutMs, 1000, 300000);
125
+ integer(input.wake.maxRuns, 1, 10000);
126
+ }
127
+ if (input.repositoryDelivery !== undefined) {
128
+ if (input.version !== 2)
129
+ fail('repository delivery requires task version 2');
130
+ shape(input.repositoryDelivery, ['repository', 'baseBranch', 'branch', 'paths', 'expiresAt', 'maxActions', 'maxTotalBytes', 'openPullRequest'], ['credentialHandle', 'externalGrantId', 'acceptance', 'outcome', 'events']);
131
+ const value = input.repositoryDelivery;
132
+ if (value.acceptance !== undefined && !['goal-outcome', 'goal-step'].includes(value.acceptance))
133
+ fail('invalid repository acceptance');
134
+ for (const key of ['repository', 'baseBranch', 'branch'])
135
+ if (typeof value[key] !== 'string' || value[key].length === 0 || value[key].length > 256)
136
+ fail('invalid repository delivery');
137
+ if ((typeof value.credentialHandle === 'string') === (typeof value.externalGrantId === 'string')
138
+ || value.credentialHandle !== undefined && (value.credentialHandle.length === 0 || value.credentialHandle.length > 256)
139
+ || value.externalGrantId !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(value.externalGrantId))
140
+ fail('repository delivery requires exactly one credentialHandle or externalGrantId');
141
+ if (!/^[A-Za-z0-9][A-Za-z0-9-]*\/[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value.repository) || !Array.isArray(value.paths) || value.paths.length !== 1 || value.paths[0] !== verification.artifactPath
142
+ || value.baseBranch === value.branch || !Number.isSafeInteger(value.expiresAt) || typeof value.openPullRequest !== 'boolean')
143
+ fail('invalid repository delivery');
144
+ integer(value.maxActions, value.openPullRequest ? 3 : 2, 10_000);
145
+ integer(value.maxTotalBytes, 1, 64 * 1024 * 1024);
146
+ if (value.events !== undefined) {
147
+ shape(value.events, ['credentialHandle', 'maxPolls', 'maxFires', 'pollIntervalMs', 'requestTimeoutMs']);
148
+ if (value.externalGrantId !== undefined || !value.outcome || typeof value.events.credentialHandle !== 'string' || !/^[a-z0-9][a-z0-9._:-]{0,199}$/u.test(value.events.credentialHandle))
149
+ fail('repository events require outcome and an existing observation credential');
150
+ integer(value.events.maxPolls, 2, 10000);
151
+ integer(value.events.maxFires, 1, 100);
152
+ integer(value.events.pollIntervalMs, 1000, 3600000);
153
+ integer(value.events.requestTimeoutMs, 100, 30000);
154
+ if (value.events.maxPolls <= value.events.maxFires || input.maxGoalRounds < 2)
155
+ fail('repository event budget cannot cover observation and continuation');
156
+ // Each native round may create one outcome assessment with up to three verifier attempts.
157
+ integer(value.maxActions, 3 + 12 * input.maxGoalRounds, 10000);
158
+ if (input.wake && input.wake.maxRuns < value.events.maxFires + 1)
159
+ fail('wake budget must cover delivery and allowed event continuations');
160
+ }
161
+ if (value.outcome !== undefined) {
162
+ shape(value.outcome, ['requiredChecks', 'reviewerIds', 'minApprovals', 'timeoutMs', 'freshnessMs']);
163
+ if (value.acceptance !== 'goal-step' || !value.openPullRequest)
164
+ fail('repository outcome requires explicit goal-step delivery and a pull request');
165
+ createVerifierAuthorities({ authorities: [{ ...value.outcome, kind: 'repository-readback', id: 'repository-validation', grantId: 'repository-validation', grantRevision: 1, repository: value.repository, branch: value.branch, baseBranch: value.baseBranch }] });
166
+ integer(value.maxActions, 7, 10_000);
167
+ if (budget.durationMs <= input.stepMaxDurationMs + verificationWindow + value.outcome.timeoutMs)
168
+ fail('execution budget cannot cover repository verification');
169
+ }
170
+ }
171
+ return input;
172
+ }
173
+ function map(value) { if (!isMap(value))
174
+ fail('profile config must be a mapping'); return value; }
175
+ function sequence(value) { if (!isSeq(value))
176
+ fail('expected profile list'); return value; }
177
+ function untagged(value, label) {
178
+ if (isScalar(value)) {
179
+ if (value.tag !== undefined)
180
+ fail(`tagged ${label}`);
181
+ return;
182
+ }
183
+ if (isSeq(value)) {
184
+ if (value.tag !== undefined)
185
+ fail(`tagged ${label}`);
186
+ for (const item of value.items)
187
+ untagged(item, label);
188
+ return;
189
+ }
190
+ if (isMap(value)) {
191
+ if (value.tag !== undefined)
192
+ fail(`tagged ${label}`);
193
+ for (const pair of value.items) {
194
+ if (!isScalar(pair.key) || pair.key.tag !== undefined || typeof pair.key.value !== 'string' || pair.value === null)
195
+ fail(`invalid ${label}`);
196
+ untagged(pair.value, label);
197
+ }
198
+ return;
199
+ }
200
+ fail(`invalid ${label}`);
201
+ }
202
+ function literalString(value) {
203
+ return isScalar(value) && value.tag === undefined && typeof value.value === 'string' ? value.value : undefined;
204
+ }
205
+ function literalInteger(value) {
206
+ return isScalar(value) && value.tag === undefined && typeof value.value === 'number' && Number.isSafeInteger(value.value) ? value.value : undefined;
207
+ }
208
+ /** Accept only the public, credential-free broker projection used for this admission. */
209
+ function externalProjection(value) {
210
+ const record = (input, required, optional = []) => {
211
+ if (input === null || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => !required.includes(key) && !optional.includes(key)) || required.some(key => !Object.hasOwn(input, key)))
212
+ fail('invalid external repository grant');
213
+ return input;
214
+ };
215
+ const text = (input) => typeof input === 'string' && input.length > 0 && input.length <= 4096 && !/[\p{Cc}]/u.test(input);
216
+ const positive = (input) => typeof input === 'number' && Number.isSafeInteger(input) && input > 0;
217
+ const grant = record(value, ['id', 'revision', 'grantDigest', 'owner', 'sessionId', 'destination', 'expiresAt', 'maxActions', 'maxTotalBytes', 'source', 'maxCostUnits', 'allowedOperations', 'allowedInspectKinds'], ['verifiedDelivery']);
218
+ const owner = record(grant.owner, ['principalDigest', 'principalRecordId', 'principalVersion', 'workspace', 'preset', 'bindingId', 'bindingVersion', 'bindingGeneration']);
219
+ const destination = record(grant.destination, ['classification', 'repository', 'branch', 'paths'], ['baseBranch']);
220
+ const source = record(grant.source, ['classification', 'provenanceDigest']);
221
+ const delivery = grant.verifiedDelivery === undefined ? undefined : record(grant.verifiedDelivery, ['ownerRouteId', 'budgetId'], ['acceptance']);
222
+ if (!text(grant.id) || !positive(grant.revision) || typeof grant.grantDigest !== 'string' || !/^[0-9a-f]{64}$/u.test(grant.grantDigest) || !text(grant.sessionId)
223
+ || !text(owner.principalDigest) || !text(owner.principalRecordId) || !positive(owner.principalVersion) || !text(owner.workspace) || !text(owner.preset) || !text(owner.bindingId) || !positive(owner.bindingVersion) || !positive(owner.bindingGeneration)
224
+ || destination.classification !== 'github-repository' || !text(destination.repository) || !text(destination.branch) || destination.baseBranch !== undefined && !text(destination.baseBranch)
225
+ || !Array.isArray(destination.paths) || destination.paths.length < 1 || destination.paths.some(path => !text(path)) || !positive(grant.expiresAt) || !positive(grant.maxActions) || !positive(grant.maxTotalBytes)
226
+ || source.classification === undefined || typeof source.provenanceDigest !== 'string' || !/^[0-9a-f]{64}$/u.test(source.provenanceDigest) || !positive(grant.maxCostUnits)
227
+ || !Array.isArray(grant.allowedOperations) || grant.allowedOperations.some(operation => typeof operation !== 'string') || !Array.isArray(grant.allowedInspectKinds) || grant.allowedInspectKinds.some(kind => typeof kind !== 'string')
228
+ || delivery !== undefined && (!text(delivery.ownerRouteId) || !text(delivery.budgetId) || delivery.acceptance !== undefined && !['goal-outcome', 'goal-step'].includes(String(delivery.acceptance))))
229
+ fail('invalid external repository grant');
230
+ return { id: grant.id, revision: grant.revision, grantDigest: grant.grantDigest, owner: owner, sessionId: grant.sessionId,
231
+ destination: { repository: destination.repository, branch: destination.branch, ...(destination.baseBranch === undefined ? {} : { baseBranch: destination.baseBranch }), paths: [...destination.paths] }, expiresAt: grant.expiresAt, maxActions: grant.maxActions, maxTotalBytes: grant.maxTotalBytes,
232
+ allowedOperations: [...grant.allowedOperations], allowedInspectKinds: [...grant.allowedInspectKinds], ...(delivery === undefined ? {} : { verifiedDelivery: delivery }) };
233
+ }
234
+ function parse(source) {
235
+ const document = parseDocument(source.trim() || '[]', { customTags: [{ tag: 'tag:yaml.org,2002:js', resolve: (value) => value }] });
236
+ if (document.errors.length || !isSeq(document.contents))
237
+ fail('profile must be an unambiguous YAML sequence');
238
+ return { document, rows: document.contents };
239
+ }
240
+ function merge(base, overlay) {
241
+ const result = map(base.clone());
242
+ for (const pair of overlay.items) {
243
+ if (!isScalar(pair.key) || typeof pair.key.value !== 'string')
244
+ fail('profile keys must be strings');
245
+ const inherited = result.get(pair.key.value, true);
246
+ const value = pair.value;
247
+ result.set(pair.key.value, isMap(inherited) && isMap(value) ? merge(inherited, value) : value?.clone() ?? null);
248
+ }
249
+ return result;
250
+ }
251
+ /** Compose a complete candidate, preserving custom siblings and rejecting modified managed settings. */
252
+ export function prepareGoalAdmission(input, source, effectiveSource, taskSource, snapshot, now = Date.now(), settingsSource, eventSupport) {
253
+ const task = parseGoalAdmissionTask(taskSource);
254
+ for (const value of [input.dshHome, input.workspace])
255
+ if (!isAbsolute(value) || normalize(value) !== value)
256
+ fail('home and workspace must be canonical paths');
257
+ if (!Number.isFinite(now) || task.version === 1 && now >= Date.parse(DEEPSEEK_CHAT_COMPLETIONS_CONTRACT.expiresAt))
258
+ fail('model contract expired');
259
+ const target = parse(source);
260
+ const effective = parse(effectiveSource);
261
+ const row = (rows, slug, required) => {
262
+ const id = `dsh-enhanced-${slug}`;
263
+ const name = `@dsh-enhanced/${slug}`;
264
+ const found = rows.items.filter(value => isMap(value) && (value.get('id') === id || value.get('name') === name));
265
+ if (found.length > 1)
266
+ fail(`duplicate ${slug}`);
267
+ const item = found[0];
268
+ if (!item) {
269
+ if (required)
270
+ fail(`install ${name} first`);
271
+ return undefined;
272
+ }
273
+ if (item.get('id') !== id || item.has('name') && item.get('name') !== name || item.has('disabled') && item.get('disabled') !== false)
274
+ fail(`disabled or shadowed ${slug}`);
275
+ return item;
276
+ };
277
+ const config = (slug, materialize = true) => {
278
+ const inherited = row(effective.rows, slug, true);
279
+ const existing = row(target.rows, slug, false);
280
+ const base = inherited.has('config') ? map(inherited.get('config', true)) : target.document.createNode({});
281
+ const value = existing?.has('config') ? merge(base, map(existing.get('config', true))) : map(base.clone());
282
+ if (materialize) {
283
+ const destination = existing ?? target.document.createNode({ id: `dsh-enhanced-${slug}`, name: `@dsh-enhanced/${slug}` });
284
+ destination.set('config', value);
285
+ if (!existing)
286
+ target.rows.add(destination);
287
+ }
288
+ return value;
289
+ };
290
+ // Include complete required rows in the candidate before inspecting its inherited isolation scope.
291
+ for (const slug of ['assistant-isolation', 'assistant-web-owner'])
292
+ config(slug);
293
+ const delivery = config('assistant-delivery');
294
+ const goals = config('assistant-goals');
295
+ const verifier = config('assistant-verifier');
296
+ const actions = task.repositoryDelivery ? config('assistant-actions') : undefined;
297
+ const keychain = task.repositoryDelivery?.credentialHandle === undefined ? undefined : config('credentials-keychain', false);
298
+ const eventTriggers = task.repositoryDelivery?.events ? config('event-triggers') : undefined;
299
+ const provider = task.version === 1 ? config('assistant-deepseek-budget') : undefined;
300
+ const personal = config('personal-assistant');
301
+ const policy = map(personal.get('assistantPolicy', true));
302
+ const principalId = `web/${input.profile}/local/operator`;
303
+ const profile = inspectAutonomyProfile(target.document.toString(), input.profile, input.dshHome);
304
+ const { binding, owner } = snapshot;
305
+ if (profile.grant.workspace !== input.workspace || profile.grant.agentPreset !== input.preset || binding.workspace !== input.workspace || binding.agentPreset !== input.preset
306
+ || !isDeepStrictEqual(binding.principal, profile.principal) || owner.id !== profile.grant.principalRecordId || owner.version !== profile.grant.principalVersion
307
+ || !isDeepStrictEqual(owner.principal, profile.principal) || owner.role !== 'owner' || owner.status !== 'active'
308
+ || now + task.executionBudget.durationMs > profile.grant.expiresAt)
309
+ fail('owner, scope or remaining grant deadline mismatch');
310
+ const admissionId = `goal-${createHash('sha256').update(JSON.stringify([input.profile, binding.id, owner.id, owner.version, task.objective])).digest('hex').slice(0, 24)}`;
311
+ const wake = task.wake ?? (task.repositoryDelivery ? { maxDelayMs: task.repositoryDelivery.events ? task.executionBudget.durationMs - 1 : Math.min(60_000, task.executionBudget.durationMs - 1), runTimeoutMs: Math.min(task.repositoryDelivery.events ? 300_000 : 60_000, task.executionBudget.durationMs), maxRuns: (task.repositoryDelivery.events?.maxFires ?? 0) + 1 } : undefined);
312
+ if (task.repositoryDelivery && (task.repositoryDelivery.expiresAt <= now + task.executionBudget.durationMs + 60_000 || task.repositoryDelivery.expiresAt > profile.grant.expiresAt))
313
+ fail('repository delivery deadline mismatch');
314
+ const repository = task.repositoryDelivery;
315
+ const externalGrant = (() => {
316
+ if (repository?.externalGrantId === undefined)
317
+ return undefined;
318
+ const configured = actions.toJSON();
319
+ if (configured.broker?.mode !== 'external-unix-v1' || !Array.isArray(configured.grants) || configured.grants.length !== 0 || !Array.isArray(configured.externalGrants))
320
+ fail('external repository grant requires external-unix-v1');
321
+ const matches = configured.externalGrants.map(externalProjection).filter(grant => grant.id === repository.externalGrantId);
322
+ if (matches.length !== 1)
323
+ fail('external repository grant is unavailable');
324
+ const grant = matches[0];
325
+ const expectedOwner = { principalDigest: createHash('sha256').update(principalId).digest('hex'), principalRecordId: owner.id, principalVersion: owner.version,
326
+ workspace: input.workspace, preset: input.preset, bindingId: binding.id, bindingVersion: binding.version, bindingGeneration: binding.generation };
327
+ const expectedOperations = ['commit', 'inspect', ...(repository.openPullRequest ? ['pull-request'] : [])];
328
+ const expectedInspections = ['repository', 'branch', 'file', ...(repository.outcome ? ['pull-request', 'checks', 'reviews'] : [])];
329
+ const acceptance = repository.acceptance ?? 'goal-outcome';
330
+ if (!isDeepStrictEqual(grant.owner, expectedOwner) || grant.sessionId !== binding.sessionId
331
+ || grant.destination.repository !== repository.repository || grant.destination.branch !== repository.branch || grant.destination.baseBranch !== repository.baseBranch
332
+ || !isDeepStrictEqual(grant.destination.paths, repository.paths) || grant.expiresAt !== repository.expiresAt
333
+ || grant.maxActions !== repository.maxActions || grant.maxTotalBytes !== repository.maxTotalBytes
334
+ || !isDeepStrictEqual([...grant.allowedOperations].sort(), expectedOperations.sort())
335
+ || !isDeepStrictEqual([...grant.allowedInspectKinds].sort(), expectedInspections.sort())
336
+ || grant.verifiedDelivery === undefined || grant.verifiedDelivery.ownerRouteId !== admissionId
337
+ || grant.verifiedDelivery.budgetId !== `${admissionId}-runs` || (grant.verifiedDelivery.acceptance ?? 'goal-outcome') !== acceptance)
338
+ fail('external repository grant does not exactly match this admission');
339
+ return grant;
340
+ })();
341
+ const managed = isSeq(verifier.get('profiles', true)) && sequence(verifier.get('profiles', true)).items.some(value => isMap(value) && String(value.get('id')).startsWith('goal-'));
342
+ const set = (config, field, desired, defaults = []) => {
343
+ if (config.has(field)) {
344
+ const old = config.get(field, true);
345
+ if (isScalar(old) && old.tag !== undefined)
346
+ fail(`tagged managed ${field}`);
347
+ const current = isMap(old) || isSeq(old) ? old.toJSON() : isScalar(old) ? old.value : old;
348
+ if (!isDeepStrictEqual(current, desired) && (managed || !defaults.some(value => isDeepStrictEqual(current, value))))
349
+ fail(`existing ${field} differs; no configuration was changed`);
350
+ }
351
+ config.set(field, target.document.createNode(desired));
352
+ };
353
+ const append = (config, field, entries) => {
354
+ let values = config.get(field, true);
355
+ if (values === undefined) {
356
+ values = target.document.createNode([]);
357
+ config.set(field, values);
358
+ }
359
+ if (!isSeq(values))
360
+ fail(`invalid ${field}`);
361
+ untagged(values, field);
362
+ for (const entry of entries) {
363
+ const matches = values.items.filter(value => isMap(value) && value.get('id') === entry.id);
364
+ if (matches.length > 1 || matches.length === 1 && !isDeepStrictEqual(matches[0].toJSON(), entry))
365
+ fail(`existing ${field} entry differs`);
366
+ if (matches.length === 0)
367
+ values.add(target.document.createNode(entry));
368
+ }
369
+ };
370
+ const authority = { kind: 'isolated-runner', id: `${admissionId}-verify`, stateRoot: join(input.dshHome, 'assistant-goal-verification', input.profile, admissionId),
371
+ image: profile.image, dockerPath: profile.dockerPath, command: task.verification.command, expiresAt: profile.grant.expiresAt,
372
+ maxRuns: task.verification.maxRuns, maxTotalDurationMs: task.verification.maxTotalDurationMs, maxDurationMs: task.verification.maxDurationMs,
373
+ maxOutputBytes: task.verification.maxOutputBytes, testSets: [{ id: 'cases', cases: task.verification.cases }] };
374
+ const remoteAuthority = repository?.outcome === undefined ? undefined : {
375
+ ...repository.outcome, kind: 'repository-readback', id: `${admissionId}-repository-verify`, grantId: externalGrant?.id ?? `${admissionId}-repository`, grantRevision: externalGrant?.revision ?? 1,
376
+ repository: repository.repository, branch: repository.branch, baseBranch: repository.baseBranch,
377
+ };
378
+ const authorities = remoteAuthority ? [authority, remoteAuthority] : [authority];
379
+ const compiled = createVerifierAuthorities({ authorities });
380
+ const verificationWindow = task.verification.maxDurationMs * task.verification.cases.length;
381
+ const profiles = ['goal-step', 'goal-outcome'].map(taskKind => ({ id: `${admissionId}-${taskKind}`, version: 1, taskKind,
382
+ objective: task.objective, scope: { workspace: input.workspace, preset: input.preset }, owner: { principalRecordId: owner.id, principalVersion: owner.version },
383
+ validityMs: task.executionBudget.durationMs, bounds: { maxDurationMs: taskKind === 'goal-outcome' && remoteAuthority ? remoteAuthority.timeoutMs : verificationWindow, maxEvidenceBytes: 8192 },
384
+ criteria: taskKind === 'goal-outcome' && remoteAuthority
385
+ ? [{ id: 'repository-ready', kind: 'target-readback', authority: { id: compiled[1].id, digest: compiled[1].digest }, objectId: `${remoteAuthority.repository}:${remoteAuthority.branch}`, expected: [{ pointer: '/ready', value: true }] }]
386
+ : [{ id: 'artifact-behavior', kind: 'isolated-process-behavior', authority: { id: compiled[0].id, digest: compiled[0].digest }, artifactPath: task.verification.artifactPath, testSetId: 'cases' }] }));
387
+ append(verifier, 'authorities', authorities);
388
+ append(verifier, 'profiles', profiles);
389
+ // Compile all effective authorities/profiles, detecting conflicting exact task matches too.
390
+ compileAcceptanceProfiles({ databasePath: 'validation-only', authorities: sequence(verifier.get('authorities', true)).toJSON(), profiles: sequence(verifier.get('profiles', true)).toJSON() });
391
+ set(goals, 'verifyNativeRounds', true, [false]);
392
+ set(goals, 'verifyGoalOutcome', true, [false]);
393
+ set(goals, 'preauthorizedCreateMaxRounds', task.maxGoalRounds, [0]);
394
+ set(goals, 'stepMaxDurationMs', task.stepMaxDurationMs, [60000]);
395
+ set(goals, 'executionBudget', task.executionBudget);
396
+ if (task.strategy !== undefined) {
397
+ set(goals, 'strategy', task.strategy);
398
+ append(policy, 'rules', [
399
+ { id: `${admissionId}-strategy-goal`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['delegate'], resource: { kind: 'goal', id: 'business-context' }, context: { initiators: wake === undefined ? ['external'] : ['external', 'background'] } },
400
+ { id: `${admissionId}-strategy-tool`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: 'goal_strategy' }, context: { initiators: wake === undefined ? ['external'] : ['external', 'background'] } },
401
+ ]);
402
+ }
403
+ const defaults = effective.rows.items.filter(value => isMap(value) && value.get('id') === 'agent-default-model');
404
+ const overrides = target.rows.items.filter(value => isMap(value) && value.get('id') === 'agent-default-model');
405
+ if (defaults.length > 1 || overrides.length > 1)
406
+ fail('ambiguous default model');
407
+ if (task.version === 1) {
408
+ if (!provider)
409
+ fail('DeepSeek provider configuration is unavailable');
410
+ set(provider, 'enabled', true, [false]);
411
+ set(provider, 'apiKeyEnv', task.apiKeyEnv ?? 'DEEPSEEK_API_KEY', ['DEEPSEEK_API_KEY']);
412
+ set(provider, 'defaultMaxTokens', task.executionBudget.maxOutputTokensPerCall, [8192]);
413
+ if (!managed) {
414
+ delivery.set('agentProvider', DEEPSEEK_PROVIDER);
415
+ delivery.set('agentModel', task.model);
416
+ delivery.set('agentMaxOutputTokens', task.executionBudget.maxOutputTokensPerCall);
417
+ }
418
+ else {
419
+ set(delivery, 'agentProvider', DEEPSEEK_PROVIDER);
420
+ set(delivery, 'agentModel', task.model);
421
+ set(delivery, 'agentMaxOutputTokens', task.executionBudget.maxOutputTokensPerCall);
422
+ }
423
+ const modelRow = overrides[0] ?? target.document.createNode({ id: 'agent-default-model' });
424
+ if (modelRow.has('disabled') && modelRow.get('disabled') !== false)
425
+ fail('default model plugin is disabled');
426
+ if (managed && modelRow.has('config') && !isDeepStrictEqual(map(modelRow.get('config', true)).toJSON(), { provider: DEEPSEEK_PROVIDER, model: task.model }))
427
+ fail('existing default model differs');
428
+ modelRow.set('config', target.document.createNode({ provider: DEEPSEEK_PROVIDER, model: task.model }));
429
+ if (!overrides.length)
430
+ target.rows.add(modelRow);
431
+ }
432
+ else {
433
+ const settingsRoute = settingsSource === undefined ? undefined : parseSettingsDefaultModelRoute(settingsSource);
434
+ const modelRow = defaults[0];
435
+ if (settingsRoute === undefined && (!modelRow || modelRow.has('disabled') && modelRow.get('disabled') !== false || !modelRow.has('config')))
436
+ fail('configured default model route is unavailable');
437
+ const configured = settingsRoute ?? map(modelRow.get('config', true)).toJSON();
438
+ if (!isDeepStrictEqual(configured, task.route))
439
+ fail('task route is not the configured default provider/model');
440
+ // Scheduled Delivery does not read Web's default-model settings. Pin its
441
+ // fallback to the already configured and admitted public route as well.
442
+ // Credentials/provider configuration remains owned by the existing route.
443
+ if (wake) {
444
+ if (!managed) {
445
+ delivery.set('agentProvider', task.route.provider);
446
+ delivery.set('agentModel', task.route.model);
447
+ delivery.set('agentMaxOutputTokens', task.executionBudget.maxOutputTokensPerCall);
448
+ }
449
+ else {
450
+ set(delivery, 'agentProvider', task.route.provider);
451
+ set(delivery, 'agentModel', task.route.model);
452
+ set(delivery, 'agentMaxOutputTokens', task.executionBudget.maxOutputTokensPerCall);
453
+ }
454
+ }
455
+ }
456
+ if (wake) {
457
+ append(delivery, 'ownerRoutes', [{ id: admissionId, conversation: binding.conversation, principal: binding.principal, workspace: binding.workspace,
458
+ agentPreset: binding.agentPreset, policyRef: binding.policyRef, minimumGeneration: binding.generation }]);
459
+ const budgetId = `${admissionId}-runs`;
460
+ set(goals, 'backgroundWake', { ownerRouteId: admissionId, budgetId, maxDelayMs: wake.maxDelayMs, runTimeoutMs: wake.runTimeoutMs });
461
+ set(goals, 'preauthorizedSchedule', true, [false]);
462
+ const automation = map(personal.get('assistantAutomations', true));
463
+ set(automation, 'schedulerEnabled', true, [false]);
464
+ append(policy, 'budgets', [{ id: budgetId, metric: 'automation-runs', limit: wake.maxRuns, periodMs: Number.MAX_SAFE_INTEGER, scope: 'global' }]);
465
+ append(policy, 'rules', [
466
+ { id: `${admissionId}-goal`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['observe', 'inspect', 'snapshot', 'execute'], resource: { kind: 'goal', id: 'business-context' }, context: { initiators: ['background'] } },
467
+ ...['isolation_run', 'isolation_grants', 'goal_context'].map(tool => ({ id: `${admissionId}-${tool}`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: tool }, context: { initiators: ['background'] } })),
468
+ { id: `${admissionId}-isolation-grant`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: `isolation:${profile.grant.id}` }, context: { initiators: ['background'] } },
469
+ { id: `${admissionId}-reply`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['reply'], resource: { kind: 'message', id: binding.id }, context: { initiators: ['background'] } },
470
+ { id: `${admissionId}-automation`, effect: 'allow', subject: { kind: 'background', id: '*', workspace: input.workspace, principal: principalId }, actions: ['reconcile', 'execute'], resource: { kind: 'automation', id: 'goal-wake-*' }, context: { initiators: ['background'] } },
471
+ { id: `${admissionId}-resume`, effect: 'allow', subject: { kind: 'background', id: 'assistant-goals-wake/v1', workspace: input.workspace, principal: principalId }, actions: ['wake'], resource: { kind: 'goal', id: 'business-context' }, context: { initiators: ['background'] } },
472
+ ]);
473
+ if (task.repositoryDelivery) {
474
+ const repository = task.repositoryDelivery;
475
+ let grantId = externalGrant?.id;
476
+ if (!externalGrant) {
477
+ const handles = keychain.get('handles', true);
478
+ if (!isSeq(handles))
479
+ fail('credential handle is unavailable');
480
+ untagged(handles, 'credential handles');
481
+ const matching = handles.items.filter(item => isMap(item) && literalString(item.get('id', true)) === repository.credentialHandle);
482
+ if (matching.length !== 1)
483
+ fail('credential handle is unavailable');
484
+ const consumers = matching[0].get('consumers', true);
485
+ const purposes = matching[0].get('purposes', true);
486
+ if (!isSeq(consumers) || !isSeq(purposes)
487
+ || literalString(matching[0].get('provider', true)) === undefined
488
+ || (literalInteger(matching[0].get('maxLeaseMs', true)) ?? 0) < 30_000
489
+ || !consumers.items.some(item => literalString(item) === 'dsh-enhanced-assistant-actions')
490
+ || !purposes.items.some(item => literalString(item) === 'github.commit'))
491
+ fail('credential handle is unavailable');
492
+ const grant = { id: `${admissionId}-repository`, revision: 1, principalDigest: createHash('sha256').update(principalId).digest('hex'), principalRecordId: owner.id, principalVersion: owner.version, workspace: input.workspace, agentPreset: input.preset, repository: repository.repository, branch: repository.branch, paths: repository.paths, credentialHandle: repository.credentialHandle, expiresAt: repository.expiresAt, maxActions: repository.maxActions, maxTotalBytes: repository.maxTotalBytes, repoWorkflow: { baseBranch: repository.baseBranch, allowBranchCreate: false, allowPullRequest: repository.openPullRequest }, verifiedDelivery: { ownerRouteId: admissionId, budgetId, ...(repository.acceptance ? { acceptance: repository.acceptance } : {}) } };
493
+ append(actions, 'grants', [grant]);
494
+ if (typeof Actions.validateActionConfig !== 'function')
495
+ fail('install matching @dsh-enhanced/assistant-actions first');
496
+ Actions.validateActionConfig(actions.toJSON());
497
+ grantId = grant.id;
498
+ }
499
+ const repositoryRules = [
500
+ { id: `${admissionId}-repository-agent`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: `action:github:${grantId}` }, context: { initiators: ['external', 'background'] } },
501
+ ...['action_github_grants', 'action_github_inspect', 'action_github_deliver', 'action_github_delivery_status'].map(tool => ({ id: `${admissionId}-repository-${tool}`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: tool }, context: { initiators: ['external', 'background'] } })),
502
+ { id: `${admissionId}-repository-background`, effect: 'allow', subject: { kind: 'background', id: 'dsh-enhanced-assistant-actions', workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: `action:github:${grantId}` }, context: { initiators: ['background'] } },
503
+ { id: `${admissionId}-repository-automation`, effect: 'allow', subject: { kind: 'background', id: '*', workspace: input.workspace, principal: principalId }, actions: ['reconcile', 'execute'], resource: { kind: 'automation', id: 'verified-delivery-*' }, context: { initiators: ['background'] } },
504
+ { id: `${admissionId}-repository-notice`, effect: 'allow', subject: { kind: 'background', id: 'assistant-actions-verified-delivery/v1', workspace: input.workspace, principal: principalId }, actions: ['send'], resource: { kind: 'message', id: binding.id }, context: { initiators: ['background'] } },
505
+ ...(externalGrant ? [] : [{ id: `${admissionId}-repository-credential`, effect: 'allow', subject: { kind: 'background', id: 'dsh-enhanced-assistant-actions' }, actions: ['credential.use'], resource: { kind: 'credential', id: repository.credentialHandle }, context: { initiators: ['background'] } }]),
506
+ ];
507
+ append(policy, 'rules', repositoryRules);
508
+ if (repository.events) {
509
+ if (typeof eventSupport?.normalizeEventTriggersConfig !== 'function' || typeof eventSupport.EVENT_OBSERVER_EXECUTOR !== 'string')
510
+ fail('install matching event-triggers support for repository events');
511
+ const { normalizeEventTriggersConfig, EVENT_OBSERVER_EXECUTOR } = eventSupport;
512
+ const events = repository.events, triggerId = `${admissionId}-repository-events`, automationId = `${triggerId}-source`, pollBudgetId = `${triggerId}-polls`, eventBudgetId = `${triggerId}-runs`;
513
+ const eventHandles = keychain.get('handles', true);
514
+ if (!isSeq(eventHandles))
515
+ fail('repository observation credential handle is unavailable');
516
+ untagged(eventHandles, 'credential handles');
517
+ const eventHandle = eventHandles.items.filter(item => isMap(item) && literalString(item.get('id', true)) === events.credentialHandle);
518
+ if (eventHandle.length !== 1)
519
+ fail('repository observation credential handle is unavailable');
520
+ const eventConsumers = eventHandle[0].get('consumers', true), eventPurposes = eventHandle[0].get('purposes', true);
521
+ if (!isSeq(eventConsumers) || !isSeq(eventPurposes) || (literalInteger(eventHandle[0].get('maxLeaseMs', true)) ?? 0) < events.requestTimeoutMs
522
+ || !eventConsumers.items.some(item => literalString(item) === 'dsh-enhanced-event-triggers') || !eventPurposes.items.some(item => literalString(item) === 'github.observe'))
523
+ fail('repository observation handle must authorize event-triggers and github.observe');
524
+ const observer = { workspace: input.workspace, preset: input.preset, principalId, principalRecordId: owner.id, principalVersion: owner.version, ownerRouteId: admissionId, expiresAt: repository.expiresAt, budgetId: eventBudgetId };
525
+ append(eventTriggers, 'triggers', [{ id: triggerId, automationId, kind: 'github-repository', observerLifetime: 'goal', repository: repository.repository, branch: repository.branch, baseBranch: repository.baseBranch, credentialHandle: events.credentialHandle,
526
+ fireWhen: 'changed', debounceMs: 0, cooldownMs: 0, maxFires: events.maxFires, observer }]);
527
+ set(eventTriggers, 'pollerEnabled', true, [false]);
528
+ set(eventTriggers, 'pollIntervalMs', events.pollIntervalMs, [5000]);
529
+ set(eventTriggers, 'requestTimeoutMs', events.requestTimeoutMs, [10000]);
530
+ set(goals, 'eventWaits', true, [false]);
531
+ append(policy, 'budgets', [{ id: pollBudgetId, metric: 'repository-observations', limit: events.maxPolls, periodMs: Number.MAX_SAFE_INTEGER, scope: 'subject' },
532
+ { id: eventBudgetId, metric: 'automation-runs', limit: events.maxFires, periodMs: Number.MAX_SAFE_INTEGER, scope: 'subject' }]);
533
+ append(policy, 'rules', [
534
+ { id: `${triggerId}-observe`, effect: 'allow', subject: { kind: 'background', id: `event-triggers:${triggerId}`, workspace: input.workspace, principal: principalId }, actions: ['observe'], resource: { kind: 'network', id: `https://api.github.com/repos/${repository.repository}` }, context: { initiators: ['background'] }, budget: { id: pollBudgetId, amount: 1 } },
535
+ { id: `${triggerId}-credential`, effect: 'allow', subject: { kind: 'background', id: 'dsh-enhanced-event-triggers' }, actions: ['credential.use'], resource: { kind: 'credential', id: events.credentialHandle }, context: { initiators: ['background'] } },
536
+ { id: `${triggerId}-ingest`, effect: 'allow', subject: { kind: 'external', id: `event-triggers:${triggerId}`, workspace: input.workspace }, actions: ['ingest'], resource: { kind: 'automation', id: automationId }, context: { initiators: ['external'] } },
537
+ { id: `${triggerId}-host`, effect: 'allow', subject: { kind: 'background', id: EVENT_OBSERVER_EXECUTOR, workspace: input.workspace, principal: principalId }, actions: ['observe', 'reconcile', 'execute', 'pause'], resource: { kind: 'automation', id: automationId }, context: { initiators: ['background'] } },
538
+ { id: `${triggerId}-execute`, effect: 'allow', subject: { kind: 'background', id: automationId, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'automation', id: automationId }, context: { initiators: ['background'] } },
539
+ { id: `${triggerId}-wait`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['wait-for-event'], resource: { kind: 'automation', id: automationId }, context: { initiators: ['external', 'background'] } },
540
+ { id: `${triggerId}-background-wait`, effect: 'allow', subject: { kind: 'background', id: 'assistant-goals-wake/v1', workspace: input.workspace, principal: principalId }, actions: ['wait-for-event'], resource: { kind: 'automation', id: automationId }, context: { initiators: ['background'] } },
541
+ { id: `${triggerId}-wake`, effect: 'allow', subject: { kind: 'background', id: '*', workspace: input.workspace, principal: principalId }, actions: ['reconcile', 'execute'], resource: { kind: 'automation', id: 'goal-event-wake-*' }, context: { initiators: ['background'] } },
542
+ { id: `${triggerId}-native-wait`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['wait', 'pause'], resource: { kind: 'goal', id: 'business-context' }, context: { initiators: ['background'] } },
543
+ { id: `${triggerId}-wait-tool`, effect: 'allow', subject: { kind: 'agent', id: input.preset, workspace: input.workspace, principal: principalId }, actions: ['execute'], resource: { kind: 'tool', id: 'goal_wait_event' }, context: { initiators: ['external', 'background'] } },
544
+ ]);
545
+ normalizeEventTriggersConfig({ ...eventTriggers.toJSON(), databasePath: literalPath(eventTriggers.get('databasePath', true), input, 'EventTriggers databasePath') });
546
+ }
547
+ }
548
+ }
549
+ GoalsConfig(goals.toJSON());
550
+ return { patch: target.document.toString({ lineWidth: 0 }), admissionId, profile,
551
+ ...(task.repositoryDelivery ? { repositoryDelivery: { repository: task.repositoryDelivery.repository, branch: task.repositoryDelivery.branch, paths: [...task.repositoryDelivery.paths], acceptance: task.repositoryDelivery.acceptance ?? 'goal-outcome' } } : {}) };
552
+ }
553
+ //# sourceMappingURL=goal-admission.js.map