@dsh-enhanced/assistant-delivery 0.1.32 → 0.1.34
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 +11 -1
- package/lib/acceptance.d.ts +11 -3
- package/lib/acceptance.d.ts.map +1 -1
- package/lib/agent-runtime.d.ts +15 -0
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +150 -17
- package/lib/agent-runtime.js.map +1 -1
- package/lib/foreground-observation.d.ts +32 -0
- package/lib/foreground-observation.d.ts.map +1 -0
- package/lib/foreground-observation.js +13 -0
- package/lib/foreground-observation.js.map +1 -0
- package/lib/index.d.ts +6 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/reply-replay.d.ts +52 -0
- package/lib/reply-replay.d.ts.map +1 -0
- package/lib/reply-replay.js +122 -0
- package/lib/reply-replay.js.map +1 -0
- package/lib/service.d.ts +112 -2
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +429 -64
- package/lib/service.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +146 -3
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +151 -10
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +520 -7
- package/lib/store.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +14 -10
package/lib/store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID, scryptSync, timingSafeEqual } from 'node:crypto';
|
|
2
2
|
import { isAbsolute } from 'node:path';
|
|
3
3
|
import { acceptanceCanonicalJson } from '@dsh-enhanced/task-acceptance-contract';
|
|
4
|
-
import { ASSISTANT_GROWTH_CONTRACT_VERSION, WORKFLOW_TRACE_SOURCE_ID, growthObjectDigest, validateResolvedWorkflowAutomationTemplate, validateWorkflowAutomationTemplate, validateWorkflowAutomationTemplateContent, validateWorkflowTraceRevision, workflowAutomationTemplateContentDigest, workflowArgumentShapeDigest, workflowScopeKey, workflowTraceRevisionDigest, } from '@dsh-enhanced/assistant-growth-contract';
|
|
4
|
+
import { ASSISTANT_GROWTH_CONTRACT_VERSION, WORKFLOW_TRACE_SOURCE_ID, growthObjectDigest, validateResolvedWorkflowAutomationTemplate, validateWorkflowAutomationTemplate, validateWorkflowAutomationTemplateContent, validateWorkflowTraceRevision, workflowAutomationTemplateContentDigest, workflowArgumentShapeDigest, workflowScopeKey, workflowTraceRevisionDigest, WORKFLOW_OWNER_ANCHORED_PLACEHOLDER_SCHEDULE, } from '@dsh-enhanced/assistant-growth-contract';
|
|
5
5
|
import { bindingMatchesOwnerRoute, canonicalBackgroundSourceId, canonicalConversation, canonicalLocalOperatorId, canonicalOwnerRouteAuthority, canonicalPrincipal, canonicalTarget, externalPrincipalId, ownerRouteAuthorityHash, ownerRouteBindingSnapshot, } from './canonical.js';
|
|
6
6
|
import { feedbackDispatchRecoveryCode, isFeedbackDeliveryCommand, isFeedbackDispatchRecoveryCode, isExactDeliveryCommand, isLearningDeliveryCommand, isLearningDispatchRecoveryCode, isLearningRetryableFailureCode, isPermissionDeliveryCommand, isWorkflowDeliveryCommand, isWorkflowDispatchRecoveryCode, learningDispatchRecoveryCode, parseDeliveryCommand, permissionDispatchRecoveryCode, permissionDispatchRecoveryFromFailureCode, workflowDispatchRecoveryCode, } from './session-commands.js';
|
|
7
7
|
import { parseFeedbackCommand } from './feedback-command.js';
|
|
@@ -16,6 +16,24 @@ export class DeliveryStoreError extends Error {
|
|
|
16
16
|
this.name = 'DeliveryStoreError';
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* The Goals orchestration primitives that are part of goal bookkeeping rather
|
|
21
|
+
* than owner-facing business/executive tools. A successful native Goal run is
|
|
22
|
+
* reducible to the single model-turn fingerprint Automations can run only when
|
|
23
|
+
* every settled call is one of these read/planning/control primitives; any
|
|
24
|
+
* other tool (Read/Glob/Grep/Bash/Edit/executive MCP tools, ...) means the
|
|
25
|
+
* learned automation would need a real tool plane that paused materialization
|
|
26
|
+
* does not provide, so the goal abstains fail-closed.
|
|
27
|
+
*/
|
|
28
|
+
const OWNER_ANCHORED_REDUCIBLE_GOAL_TOOLS = Object.freeze(new Set([
|
|
29
|
+
'goal_strategy',
|
|
30
|
+
'goal_schedule',
|
|
31
|
+
'goal_wait_event',
|
|
32
|
+
'goal_create',
|
|
33
|
+
'goal_context',
|
|
34
|
+
'goal_checkpoint',
|
|
35
|
+
'goal_control',
|
|
36
|
+
]));
|
|
19
37
|
function workflowTraceRevision(row) {
|
|
20
38
|
let parsed;
|
|
21
39
|
try {
|
|
@@ -3062,6 +3080,46 @@ export class DeliveryStore {
|
|
|
3062
3080
|
throw new DeliveryStoreError('conflict', 'foreground acceptance binding is absent or its completion changed');
|
|
3063
3081
|
}
|
|
3064
3082
|
}
|
|
3083
|
+
recordForegroundTaskModelSelection(input) {
|
|
3084
|
+
this.assertOpen();
|
|
3085
|
+
if (typeof input.contractId !== 'string' || input.contractId.length === 0 || input.contractId.length > 512
|
|
3086
|
+
|| typeof input.provider !== 'string' || typeof input.model !== 'string'
|
|
3087
|
+
|| (input.reasoningEffort !== undefined && typeof input.reasoningEffort !== 'string')) {
|
|
3088
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground model selection is invalid');
|
|
3089
|
+
}
|
|
3090
|
+
const provider = modelRoutePart(input.provider, 'provider');
|
|
3091
|
+
const model = modelRoutePart(input.model, 'model');
|
|
3092
|
+
const effort = input.reasoningEffort === undefined ? null : modelRoutePart(input.reasoningEffort, 'effort');
|
|
3093
|
+
this.transaction(() => {
|
|
3094
|
+
const current = this.database.prepare(`SELECT status, model_selection_state, model_provider, model_id, model_reasoning_effort
|
|
3095
|
+
FROM delivery_task_acceptance_executions WHERE contract_id = ?`).get(input.contractId);
|
|
3096
|
+
if (current === undefined)
|
|
3097
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground acceptance is absent');
|
|
3098
|
+
if (current.status !== 'pending')
|
|
3099
|
+
throw new DeliveryStoreError('conflict', 'foreground acceptance model selection is no longer writable');
|
|
3100
|
+
if (current.model_selection_state === 'missing') {
|
|
3101
|
+
const changed = this.database.prepare(`UPDATE delivery_task_acceptance_executions
|
|
3102
|
+
SET model_selection_state = 'frozen', model_provider = ?, model_id = ?, model_reasoning_effort = ?
|
|
3103
|
+
WHERE contract_id = ? AND status = 'pending' AND model_selection_state = 'missing'`).run(provider, model, effort, input.contractId);
|
|
3104
|
+
if (changed.changes !== 1)
|
|
3105
|
+
throw new DeliveryStoreError('conflict', 'foreground acceptance model selection changed');
|
|
3106
|
+
return;
|
|
3107
|
+
}
|
|
3108
|
+
if (current.model_selection_state === 'frozen') {
|
|
3109
|
+
if (current.model_provider === provider && current.model_id === model && current.model_reasoning_effort === effort)
|
|
3110
|
+
return;
|
|
3111
|
+
const changed = this.database.prepare(`UPDATE delivery_task_acceptance_executions
|
|
3112
|
+
SET model_selection_state = 'inconsistent', model_provider = NULL, model_id = NULL, model_reasoning_effort = NULL
|
|
3113
|
+
WHERE contract_id = ? AND status = 'pending' AND model_selection_state = 'frozen'`).run(input.contractId);
|
|
3114
|
+
if (changed.changes !== 1)
|
|
3115
|
+
throw new DeliveryStoreError('conflict', 'foreground acceptance model selection changed');
|
|
3116
|
+
return;
|
|
3117
|
+
}
|
|
3118
|
+
if (current.model_selection_state !== 'inconsistent') {
|
|
3119
|
+
throw new DeliveryStoreError('conflict', 'foreground acceptance model selection state is invalid');
|
|
3120
|
+
}
|
|
3121
|
+
});
|
|
3122
|
+
}
|
|
3065
3123
|
inspectForegroundAcceptedExecution(contract) {
|
|
3066
3124
|
const row = this.database.prepare(`SELECT * FROM delivery_task_acceptance_executions
|
|
3067
3125
|
WHERE contract_id = ?`).get(contract.id);
|
|
@@ -3072,7 +3130,8 @@ export class DeliveryStore {
|
|
|
3072
3130
|
return null;
|
|
3073
3131
|
return Object.freeze({ contractId: row.contract_id, contractDigest: row.contract_digest,
|
|
3074
3132
|
dispatchedAt: row.dispatched_at, status: row.status, quiescent: row.quiescent === 1,
|
|
3075
|
-
completedAt: row.completed_at, executionRef: row.execution_ref
|
|
3133
|
+
completedAt: row.completed_at, executionRef: row.execution_ref, modelSelectionState: row.model_selection_state,
|
|
3134
|
+
...(row.model_selection_state === 'frozen' && row.model_provider && row.model_id ? { modelSelection: Object.freeze({ provider: row.model_provider, model: row.model_id, ...(row.model_reasoning_effort === null ? {} : { reasoningEffort: row.model_reasoning_effort }) }) } : {}) });
|
|
3076
3135
|
}
|
|
3077
3136
|
/**
|
|
3078
3137
|
* Re-read the immutable acceptance fence for one exact owner reply target.
|
|
@@ -3080,7 +3139,7 @@ export class DeliveryStore {
|
|
|
3080
3139
|
* cannot manufacture a foreground subject by naming an arbitrary Inbox.
|
|
3081
3140
|
*/
|
|
3082
3141
|
inspectForegroundAcceptedExecutionForOwner(input) {
|
|
3083
|
-
const row = this.database.prepare(`SELECT contract_id, contract_digest, dispatched_at, status, quiescent, completed_at, execution_ref
|
|
3142
|
+
const row = this.database.prepare(`SELECT contract_id, contract_digest, dispatched_at, status, quiescent, completed_at, execution_ref, model_selection_state, model_provider, model_id, model_reasoning_effort
|
|
3084
3143
|
FROM delivery_task_acceptance_executions
|
|
3085
3144
|
WHERE inbox_id = ? AND workspace = ? AND preset = ? AND principal_record_id = ?
|
|
3086
3145
|
AND principal_version = ? AND binding_id = ?`).get(input.inboxId, input.scope.workspace, input.scope.preset, input.owner.principalRecordId, input.owner.principalVersion, input.bindingId);
|
|
@@ -3088,7 +3147,120 @@ export class DeliveryStore {
|
|
|
3088
3147
|
return null;
|
|
3089
3148
|
return Object.freeze({ contractId: row.contract_id, contractDigest: row.contract_digest,
|
|
3090
3149
|
dispatchedAt: row.dispatched_at, status: row.status, quiescent: row.quiescent === 1,
|
|
3091
|
-
completedAt: row.completed_at, executionRef: row.execution_ref
|
|
3150
|
+
completedAt: row.completed_at, executionRef: row.execution_ref, modelSelectionState: row.model_selection_state,
|
|
3151
|
+
...(row.model_selection_state === 'frozen' && row.model_provider && row.model_id ? { modelSelection: Object.freeze({ provider: row.model_provider, model: row.model_id, ...(row.model_reasoning_effort === null ? {} : { reasoningEffort: row.model_reasoning_effort }) }) } : {}) });
|
|
3152
|
+
}
|
|
3153
|
+
/** Immutable pre-prompt receipt for an authenticated owner foreground turn. */
|
|
3154
|
+
bindForegroundTaskExecution(input) {
|
|
3155
|
+
this.assertOpen();
|
|
3156
|
+
if (!Number.isSafeInteger(input.dispatchedAt) || input.dispatchedAt < 0
|
|
3157
|
+
|| !Number.isSafeInteger(input.owner.principalVersion) || input.owner.principalVersion < 1
|
|
3158
|
+
|| !Number.isSafeInteger(input.binding.version) || input.binding.version < 1
|
|
3159
|
+
|| !Number.isSafeInteger(input.binding.generation) || input.binding.generation < 1) {
|
|
3160
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution binding is invalid');
|
|
3161
|
+
}
|
|
3162
|
+
const text = [input.inboxId, input.scope.workspace, input.scope.preset, input.owner.principalRecordId, input.binding.id];
|
|
3163
|
+
if (text.some(value => typeof value !== 'string' || value.trim() === '' || Buffer.byteLength(value, 'utf8') > 4_096)) {
|
|
3164
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution binding text is invalid');
|
|
3165
|
+
}
|
|
3166
|
+
this.transaction(() => {
|
|
3167
|
+
if (this.database.prepare('SELECT 1 FROM delivery_foreground_executions WHERE inbox_id = ?').get(input.inboxId) !== undefined) {
|
|
3168
|
+
throw new DeliveryStoreError('idempotency-conflict', 'foreground task has already crossed its dispatch boundary');
|
|
3169
|
+
}
|
|
3170
|
+
const admitted = this.database.prepare(`
|
|
3171
|
+
SELECT inbox.id FROM inbox_messages AS inbox
|
|
3172
|
+
JOIN conversation_bindings AS binding ON binding.id = inbox.binding_id
|
|
3173
|
+
JOIN delivery_principals AS principal ON principal.id = binding.principal_id
|
|
3174
|
+
WHERE inbox.id = ? AND inbox.status = 'claimed' AND inbox.binding_id = ?
|
|
3175
|
+
AND binding.status = 'active' AND binding.version = ? AND binding.generation = ?
|
|
3176
|
+
AND principal.status = 'active' AND principal.role = 'owner'
|
|
3177
|
+
AND principal.id = ? AND principal.version = ?
|
|
3178
|
+
AND binding.workspace = ? AND binding.agent_preset = ?
|
|
3179
|
+
`).get(input.inboxId, input.binding.id, input.binding.version, input.binding.generation, input.owner.principalRecordId, input.owner.principalVersion, input.scope.workspace, input.scope.preset);
|
|
3180
|
+
if (admitted === undefined)
|
|
3181
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution is not bound to the claimed owner inbox');
|
|
3182
|
+
this.database.prepare(`INSERT INTO delivery_foreground_executions(
|
|
3183
|
+
inbox_id, workspace, preset, principal_record_id, principal_version, binding_id, binding_version,
|
|
3184
|
+
binding_generation, dispatched_at, status, quiescent
|
|
3185
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', 0)`).run(input.inboxId, input.scope.workspace, input.scope.preset, input.owner.principalRecordId, input.owner.principalVersion, input.binding.id, input.binding.version, input.binding.generation, input.dispatchedAt);
|
|
3186
|
+
});
|
|
3187
|
+
}
|
|
3188
|
+
finishForegroundTaskExecution(input) {
|
|
3189
|
+
this.assertOpen();
|
|
3190
|
+
if (typeof input.quiescent !== 'boolean'
|
|
3191
|
+
|| !['succeeded', 'failed', 'timed-out', 'cancelled', 'unknown'].includes(input.status)
|
|
3192
|
+
|| !Number.isSafeInteger(input.completedAt) || input.completedAt < 0) {
|
|
3193
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution completion is invalid');
|
|
3194
|
+
}
|
|
3195
|
+
const status = input.quiescent && input.status === 'succeeded' ? 'succeeded' : 'unknown';
|
|
3196
|
+
const quiescent = status === 'succeeded' ? 1 : 0;
|
|
3197
|
+
const changed = this.database.prepare(`UPDATE delivery_foreground_executions
|
|
3198
|
+
SET status = ?, quiescent = ?, completed_at = ?, execution_ref = inbox_id
|
|
3199
|
+
WHERE inbox_id = ? AND status = 'pending' AND dispatched_at <= ?`).run(status, quiescent, input.completedAt, input.inboxId, input.completedAt);
|
|
3200
|
+
if (changed.changes !== 1) {
|
|
3201
|
+
const current = this.database.prepare('SELECT status, quiescent, completed_at FROM delivery_foreground_executions WHERE inbox_id = ?')
|
|
3202
|
+
.get(input.inboxId);
|
|
3203
|
+
if (current?.status === status && current.quiescent === quiescent && current.completed_at === input.completedAt)
|
|
3204
|
+
return;
|
|
3205
|
+
throw new DeliveryStoreError('conflict', 'foreground execution is absent or its completion changed');
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
recordForegroundExecutionModelSelection(input) {
|
|
3209
|
+
this.assertOpen();
|
|
3210
|
+
if (typeof input.inboxId !== 'string' || input.inboxId.length === 0 || input.inboxId.length > 512
|
|
3211
|
+
|| typeof input.provider !== 'string' || typeof input.model !== 'string'
|
|
3212
|
+
|| (input.reasoningEffort !== undefined && typeof input.reasoningEffort !== 'string')) {
|
|
3213
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution model selection is invalid');
|
|
3214
|
+
}
|
|
3215
|
+
const provider = modelRoutePart(input.provider, 'provider');
|
|
3216
|
+
const model = modelRoutePart(input.model, 'model');
|
|
3217
|
+
const effort = input.reasoningEffort === undefined ? null : modelRoutePart(input.reasoningEffort, 'effort');
|
|
3218
|
+
this.transaction(() => {
|
|
3219
|
+
const current = this.database.prepare(`SELECT status, model_selection_state, model_provider, model_id, model_reasoning_effort
|
|
3220
|
+
FROM delivery_foreground_executions WHERE inbox_id = ?`).get(input.inboxId);
|
|
3221
|
+
if (current === undefined)
|
|
3222
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground execution is absent');
|
|
3223
|
+
if (current.status !== 'pending')
|
|
3224
|
+
throw new DeliveryStoreError('conflict', 'foreground execution model selection is no longer writable');
|
|
3225
|
+
if (current.model_selection_state === 'missing') {
|
|
3226
|
+
const changed = this.database.prepare(`UPDATE delivery_foreground_executions
|
|
3227
|
+
SET model_selection_state = 'frozen', model_provider = ?, model_id = ?, model_reasoning_effort = ?
|
|
3228
|
+
WHERE inbox_id = ? AND status = 'pending' AND model_selection_state = 'missing'`).run(provider, model, effort, input.inboxId);
|
|
3229
|
+
if (changed.changes !== 1)
|
|
3230
|
+
throw new DeliveryStoreError('conflict', 'foreground execution model selection changed');
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
if (current.model_selection_state === 'frozen') {
|
|
3234
|
+
if (current.model_provider === provider && current.model_id === model && current.model_reasoning_effort === effort)
|
|
3235
|
+
return;
|
|
3236
|
+
const changed = this.database.prepare(`UPDATE delivery_foreground_executions
|
|
3237
|
+
SET model_selection_state = 'inconsistent', model_provider = NULL, model_id = NULL, model_reasoning_effort = NULL
|
|
3238
|
+
WHERE inbox_id = ? AND status = 'pending' AND model_selection_state = 'frozen'`).run(input.inboxId);
|
|
3239
|
+
if (changed.changes !== 1)
|
|
3240
|
+
throw new DeliveryStoreError('conflict', 'foreground execution model selection changed');
|
|
3241
|
+
return;
|
|
3242
|
+
}
|
|
3243
|
+
if (current.model_selection_state !== 'inconsistent')
|
|
3244
|
+
throw new DeliveryStoreError('conflict', 'foreground execution model selection state is invalid');
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
inspectForegroundExecutionForOwner(input) {
|
|
3248
|
+
const row = this.database.prepare(`SELECT execution.dispatched_at, execution.status, execution.quiescent, execution.completed_at, execution.execution_ref,
|
|
3249
|
+
execution.model_selection_state, execution.model_provider, execution.model_id, execution.model_reasoning_effort,
|
|
3250
|
+
execution.binding_version AS execution_binding_version,
|
|
3251
|
+
binding.status AS current_binding_status, binding.version AS current_binding_version, binding.generation AS current_binding_generation
|
|
3252
|
+
FROM delivery_foreground_executions AS execution
|
|
3253
|
+
JOIN conversation_bindings AS binding ON binding.id = execution.binding_id
|
|
3254
|
+
WHERE execution.inbox_id = ? AND execution.workspace = ? AND execution.preset = ? AND execution.principal_record_id = ? AND execution.principal_version = ?
|
|
3255
|
+
AND execution.binding_id = ? AND execution.binding_generation = ?`).get(input.inboxId, input.scope.workspace, input.scope.preset, input.owner.principalRecordId, input.owner.principalVersion, input.bindingId, input.bindingGeneration);
|
|
3256
|
+
if (row === undefined || row.current_binding_version !== input.bindingVersion || row.current_binding_generation !== input.bindingGeneration
|
|
3257
|
+
|| (row.execution_binding_version !== row.current_binding_version
|
|
3258
|
+
&& !(row.current_binding_status === 'revoked' && row.execution_binding_version + 1 === row.current_binding_version))
|
|
3259
|
+
|| row.completed_at === null || row.execution_ref !== input.inboxId)
|
|
3260
|
+
return null;
|
|
3261
|
+
return Object.freeze({ dispatchedAt: row.dispatched_at, status: row.status, quiescent: row.quiescent === 1,
|
|
3262
|
+
completedAt: row.completed_at, executionRef: row.execution_ref, modelSelectionState: row.model_selection_state,
|
|
3263
|
+
...(row.model_selection_state === 'frozen' && row.model_provider && row.model_id ? { modelSelection: Object.freeze({ provider: row.model_provider, model: row.model_id, ...(row.model_reasoning_effort === null ? {} : { reasoningEffort: row.model_reasoning_effort }) }) } : {}) });
|
|
3092
3264
|
}
|
|
3093
3265
|
renewInboxClaim(input) {
|
|
3094
3266
|
this.assertOpen();
|
|
@@ -3167,8 +3339,9 @@ export class DeliveryStore {
|
|
|
3167
3339
|
: existingPermissionRecovery === undefined
|
|
3168
3340
|
? undefined
|
|
3169
3341
|
: permissionDispatchRecoveryCode(existingPermissionRecovery);
|
|
3170
|
-
const
|
|
3171
|
-
|
|
3342
|
+
const foregroundDispatch = this.database.prepare(`SELECT inbox_id FROM delivery_task_acceptance_executions WHERE inbox_id = ?
|
|
3343
|
+
UNION ALL SELECT inbox_id FROM delivery_foreground_executions WHERE inbox_id = ? LIMIT 1`).get(row.id, row.id) !== undefined;
|
|
3344
|
+
const ambiguous = foregroundDispatch || row.failure_code === 'native-admission' || row.failure_code === 'native-dispatch-started'
|
|
3172
3345
|
|| (row.failure_code === 'dispatch-started'
|
|
3173
3346
|
&& permissionRecovery === undefined && !feedbackRecovery && !learningRecovery && !workflowRecovery);
|
|
3174
3347
|
const exhausted = row.attempt_count >= input.maxAttempts
|
|
@@ -5241,6 +5414,220 @@ export class DeliveryStore {
|
|
|
5241
5414
|
throw committed;
|
|
5242
5415
|
return committed;
|
|
5243
5416
|
}
|
|
5417
|
+
/**
|
|
5418
|
+
* Record a workflow learned from one Delivery-reverified owner-root successful
|
|
5419
|
+
* Goal run. Unlike the two conversation-fenced routes, there is no live
|
|
5420
|
+
* Inbox/Outbox: the caller (the Host-only Delivery service method) must have
|
|
5421
|
+
* independently re-fetched the verified Goals source and the owner-run-trace
|
|
5422
|
+
* proof, so this method never accepts a caller-supplied prompt, template, or
|
|
5423
|
+
* proof digest — all of them are rebuilt below from the verified source.
|
|
5424
|
+
*
|
|
5425
|
+
* The goal must be honestly reducible to the single built-in
|
|
5426
|
+
* `assistant.agent-turn` step that paused Growth materialization can run
|
|
5427
|
+
* (exactly one step, zero executable/business tools). Otherwise it abstains
|
|
5428
|
+
* with zero writes. The raw objective is stored only in the new table's
|
|
5429
|
+
* content_json and never enters the trace. Re-submitting the same verified
|
|
5430
|
+
* run deterministically replays its prior revision.
|
|
5431
|
+
*/
|
|
5432
|
+
commitOwnerAnchoredWorkflowTrace(input) {
|
|
5433
|
+
this.assertOpen();
|
|
5434
|
+
const digest64 = /^[a-f0-9]{64}$/u;
|
|
5435
|
+
if (typeof input !== 'object' || input === null
|
|
5436
|
+
|| typeof input.ownerRouteId !== 'string' || input.ownerRouteId === ''
|
|
5437
|
+
|| typeof input.scope?.workspace !== 'string' || typeof input.scope?.preset !== 'string'
|
|
5438
|
+
|| typeof input.goal?.id !== 'string' || typeof input.goal?.nativeGoalId !== 'string'
|
|
5439
|
+
|| typeof input.goal?.sessionId !== 'string'
|
|
5440
|
+
|| !Number.isSafeInteger(input.goal?.definition?.version) || input.goal.definition.version < 1
|
|
5441
|
+
|| typeof input.goal?.definition?.digest !== 'string' || !digest64.test(input.goal.definition.digest)
|
|
5442
|
+
|| typeof input.goal?.definition?.objective !== 'string' || input.goal.definition.objective === ''
|
|
5443
|
+
|| Buffer.byteLength(input.goal?.definition?.objective ?? '', 'utf8') > 8192
|
|
5444
|
+
|| typeof input.runId !== 'string' || input.runId === ''
|
|
5445
|
+
|| !Number.isSafeInteger(input.turn) || input.turn < 0
|
|
5446
|
+
|| typeof input.acceptance?.contractId !== 'string' || input.acceptance.contractId === ''
|
|
5447
|
+
|| typeof input.acceptance?.receiptDigest !== 'string' || !digest64.test(input.acceptance.receiptDigest)
|
|
5448
|
+
|| !Number.isSafeInteger(input.acceptance?.verifiedAt) || input.acceptance.verifiedAt < 0
|
|
5449
|
+
|| !Number.isSafeInteger(input.acceptance?.validUntil) || input.acceptance.validUntil <= input.acceptance?.verifiedAt
|
|
5450
|
+
|| typeof input.ownerRunTraceDigest !== 'string' || !digest64.test(input.ownerRunTraceDigest)
|
|
5451
|
+
|| !Array.isArray(input.steps) || input.steps.length === 0) {
|
|
5452
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow tuple is invalid');
|
|
5453
|
+
}
|
|
5454
|
+
// Fail-closed reduction gate, evaluated before any write. The learned
|
|
5455
|
+
// automation is a single no-tool model turn, so every settled call in the
|
|
5456
|
+
// successful run must be Goals bookkeeping, and even bounded failed read
|
|
5457
|
+
// probes disqualify this slice rather than being silently dropped.
|
|
5458
|
+
const reducible = input.steps.every(step => typeof step?.id === 'string' && step.id !== ''
|
|
5459
|
+
&& typeof step?.toolName === 'string'
|
|
5460
|
+
&& OWNER_ANCHORED_REDUCIBLE_GOAL_TOOLS.has(step.toolName))
|
|
5461
|
+
&& (input.failedObservations === undefined || input.failedObservations.length === 0);
|
|
5462
|
+
if (!reducible) {
|
|
5463
|
+
return Object.freeze({ outcome: 'abstained', reason: 'not-reducible', replayed: false });
|
|
5464
|
+
}
|
|
5465
|
+
return this.transaction(() => {
|
|
5466
|
+
const binding = this.getBinding(input.binding.id);
|
|
5467
|
+
const owner = binding === undefined ? undefined : this.getPrincipal(binding.principal);
|
|
5468
|
+
if (binding === undefined || binding.status !== 'active'
|
|
5469
|
+
|| binding.version !== input.binding.version || binding.generation !== input.binding.generation
|
|
5470
|
+
|| JSON.stringify(binding.conversation) !== JSON.stringify(input.binding.conversation)
|
|
5471
|
+
|| JSON.stringify(binding.principal) !== JSON.stringify(input.binding.principal)
|
|
5472
|
+
|| binding.workspace !== input.binding.workspace || binding.agentPreset !== input.binding.agentPreset
|
|
5473
|
+
|| owner?.status !== 'active' || owner.role !== 'owner') {
|
|
5474
|
+
throw new DeliveryStoreError('unauthorized-principal', 'owner-anchored workflow owner binding changed');
|
|
5475
|
+
}
|
|
5476
|
+
if (input.scope.workspace !== binding.workspace || input.scope.preset !== binding.agentPreset) {
|
|
5477
|
+
throw new DeliveryStoreError('invalid-binding', 'owner-anchored workflow scope does not match the owner binding');
|
|
5478
|
+
}
|
|
5479
|
+
const anchor = Object.freeze({
|
|
5480
|
+
scope: Object.freeze({ workspace: binding.workspace, preset: binding.agentPreset }),
|
|
5481
|
+
bindingId: binding.id,
|
|
5482
|
+
goalId: input.goal.id,
|
|
5483
|
+
definitionVersion: input.goal.definition.version,
|
|
5484
|
+
definitionDigest: input.goal.definition.digest,
|
|
5485
|
+
sessionId: input.goal.sessionId,
|
|
5486
|
+
nativeGoalId: input.goal.nativeGoalId,
|
|
5487
|
+
runId: input.runId,
|
|
5488
|
+
turn: input.turn,
|
|
5489
|
+
});
|
|
5490
|
+
const subjectRef = growthObjectDigest({
|
|
5491
|
+
contract: 'assistant-delivery-owner-anchored-workflow-subject/v1',
|
|
5492
|
+
...anchor,
|
|
5493
|
+
});
|
|
5494
|
+
const taskRef = growthObjectDigest({
|
|
5495
|
+
contract: 'assistant-delivery-owner-anchored-workflow-task-ref/v1',
|
|
5496
|
+
...anchor,
|
|
5497
|
+
acceptanceContractId: input.acceptance.contractId,
|
|
5498
|
+
acceptanceReceiptDigest: input.acceptance.receiptDigest,
|
|
5499
|
+
});
|
|
5500
|
+
const existing = this.database.prepare(`
|
|
5501
|
+
SELECT * FROM workflow_owner_anchored_templates WHERE task_ref = ?
|
|
5502
|
+
`).get(taskRef);
|
|
5503
|
+
if (existing !== undefined) {
|
|
5504
|
+
if (existing.goal_id !== input.goal.id || existing.run_id !== input.runId
|
|
5505
|
+
|| existing.turn !== input.turn || existing.session_id !== input.goal.sessionId
|
|
5506
|
+
|| existing.native_goal_id !== input.goal.nativeGoalId
|
|
5507
|
+
|| existing.owner_route_id !== input.ownerRouteId
|
|
5508
|
+
|| existing.acceptance_contract_id !== input.acceptance.contractId
|
|
5509
|
+
|| existing.acceptance_receipt_digest !== input.acceptance.receiptDigest
|
|
5510
|
+
|| existing.owner_run_trace_digest !== input.ownerRunTraceDigest) {
|
|
5511
|
+
throw new DeliveryStoreError('idempotency-conflict', 'owner-anchored task identity was reused with different evidence');
|
|
5512
|
+
}
|
|
5513
|
+
const currentRow = this.database.prepare(`
|
|
5514
|
+
SELECT revision.* FROM workflow_trace_current AS current
|
|
5515
|
+
JOIN workflow_trace_revisions AS revision
|
|
5516
|
+
ON revision.subject_ref = current.subject_ref AND revision.version = current.version
|
|
5517
|
+
WHERE current.subject_ref = ?
|
|
5518
|
+
`).get(subjectRef);
|
|
5519
|
+
if (currentRow === undefined) {
|
|
5520
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow replay is missing its trace');
|
|
5521
|
+
}
|
|
5522
|
+
const revision = workflowTraceRevision(currentRow);
|
|
5523
|
+
const replayTemplate = revision.evidence?.template;
|
|
5524
|
+
if (revision.disposition !== 'upsert' || replayTemplate === undefined
|
|
5525
|
+
|| replayTemplate.templateRef !== existing.template_ref
|
|
5526
|
+
|| replayTemplate.templateDigest !== existing.template_digest) {
|
|
5527
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow replay trace is inconsistent');
|
|
5528
|
+
}
|
|
5529
|
+
return Object.freeze({ outcome: 'trace-recorded', revision, template: replayTemplate, replayed: true });
|
|
5530
|
+
}
|
|
5531
|
+
const content = validateWorkflowAutomationTemplateContent({
|
|
5532
|
+
scope: { workspace: binding.workspace, preset: binding.agentPreset },
|
|
5533
|
+
ownerBindingId: binding.id,
|
|
5534
|
+
principalId: externalPrincipalId(binding.principal),
|
|
5535
|
+
name: 'Owner-anchored workflow',
|
|
5536
|
+
prompt: input.goal.definition.objective,
|
|
5537
|
+
schedule: WORKFLOW_OWNER_ANCHORED_PLACEHOLDER_SCHEDULE,
|
|
5538
|
+
timeoutMs: 60_000,
|
|
5539
|
+
toolCatalogIds: ['assistant.agent-turn'],
|
|
5540
|
+
deliveryBindingId: binding.id,
|
|
5541
|
+
});
|
|
5542
|
+
const templateDigest = workflowAutomationTemplateContentDigest(content);
|
|
5543
|
+
const reviewReceipt = Object.freeze({
|
|
5544
|
+
contractVersion: 1,
|
|
5545
|
+
kind: 'owner-anchored-template-review',
|
|
5546
|
+
limitation: 'deidentification-unproven',
|
|
5547
|
+
provenance: 'owner-goal-success',
|
|
5548
|
+
scheduleUnconfirmed: true,
|
|
5549
|
+
templateDigest,
|
|
5550
|
+
bindingId: binding.id,
|
|
5551
|
+
bindingVersion: binding.version,
|
|
5552
|
+
bindingGeneration: binding.generation,
|
|
5553
|
+
principalId: owner.id,
|
|
5554
|
+
ownerRouteId: input.ownerRouteId,
|
|
5555
|
+
goalId: input.goal.id,
|
|
5556
|
+
nativeGoalId: input.goal.nativeGoalId,
|
|
5557
|
+
sessionId: input.goal.sessionId,
|
|
5558
|
+
runId: input.runId,
|
|
5559
|
+
turn: input.turn,
|
|
5560
|
+
acceptanceContractId: input.acceptance.contractId,
|
|
5561
|
+
acceptanceReceiptDigest: input.acceptance.receiptDigest,
|
|
5562
|
+
ownerRunTraceDigest: input.ownerRunTraceDigest,
|
|
5563
|
+
taskRef,
|
|
5564
|
+
});
|
|
5565
|
+
const attestationDigest = growthObjectDigest({
|
|
5566
|
+
contract: 'assistant-delivery-workflow-owner-anchored-template-review/v1',
|
|
5567
|
+
receipt: reviewReceipt,
|
|
5568
|
+
});
|
|
5569
|
+
const templateRef = `workflow-template:${growthObjectDigest({
|
|
5570
|
+
contract: 'assistant-delivery-workflow-template-ref/v3',
|
|
5571
|
+
templateDigest,
|
|
5572
|
+
goalId: input.goal.id,
|
|
5573
|
+
runId: input.runId,
|
|
5574
|
+
})}`;
|
|
5575
|
+
const template = validateWorkflowAutomationTemplate({
|
|
5576
|
+
templateRef,
|
|
5577
|
+
templateDigest,
|
|
5578
|
+
privacyAttestation: {
|
|
5579
|
+
kind: 'owner-anchored',
|
|
5580
|
+
limitation: 'deidentification-unproven',
|
|
5581
|
+
provenance: 'owner-goal-success',
|
|
5582
|
+
attestationId: `workflow-owner-anchored:${attestationDigest}`,
|
|
5583
|
+
attestationDigest,
|
|
5584
|
+
},
|
|
5585
|
+
});
|
|
5586
|
+
const payload = Object.freeze({
|
|
5587
|
+
source: this.workflowTraceSourceAttestation(),
|
|
5588
|
+
scope: Object.freeze({ workspace: binding.workspace, preset: binding.agentPreset }),
|
|
5589
|
+
subjectRef,
|
|
5590
|
+
version: 1,
|
|
5591
|
+
disposition: 'upsert',
|
|
5592
|
+
evidence: Object.freeze({
|
|
5593
|
+
occurredAt: input.acceptance.verifiedAt,
|
|
5594
|
+
signal: 'owner-anchored',
|
|
5595
|
+
objectiveStatus: 'achieved',
|
|
5596
|
+
ownerBindingId: binding.id,
|
|
5597
|
+
taskRef,
|
|
5598
|
+
taskEvidenceDigest: input.ownerRunTraceDigest,
|
|
5599
|
+
template,
|
|
5600
|
+
steps: Object.freeze([Object.freeze({
|
|
5601
|
+
catalogId: 'assistant.agent-turn',
|
|
5602
|
+
argumentSchemaDigest: workflowArgumentShapeDigest({ prompt: content.prompt }),
|
|
5603
|
+
})]),
|
|
5604
|
+
}),
|
|
5605
|
+
});
|
|
5606
|
+
const revision = validateWorkflowTraceRevision({
|
|
5607
|
+
...payload,
|
|
5608
|
+
digest: workflowTraceRevisionDigest(payload),
|
|
5609
|
+
});
|
|
5610
|
+
const now = this.now();
|
|
5611
|
+
this.database.prepare(`
|
|
5612
|
+
INSERT INTO workflow_owner_anchored_templates(
|
|
5613
|
+
template_ref, template_digest, scope_key, workspace, preset, owner_binding_id,
|
|
5614
|
+
binding_version, binding_generation, principal_id, content_json, privacy_kind,
|
|
5615
|
+
privacy_attestation_id, privacy_attestation_digest, review_receipt_json, status,
|
|
5616
|
+
version, task_ref, owner_route_id, goal_id, native_goal_id, session_id, run_id, turn,
|
|
5617
|
+
acceptance_contract_id, acceptance_receipt_digest, owner_run_trace_digest,
|
|
5618
|
+
created_at, updated_at
|
|
5619
|
+
) VALUES (
|
|
5620
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'owner-anchored', ?, ?, ?, 'active', 1,
|
|
5621
|
+
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
|
5622
|
+
)
|
|
5623
|
+
`).run(template.templateRef, template.templateDigest, workflowScopeKey(content.scope), content.scope.workspace, content.scope.preset, binding.id, binding.version, binding.generation, owner.id, JSON.stringify(content), template.privacyAttestation.attestationId, template.privacyAttestation.attestationDigest, JSON.stringify(reviewReceipt), taskRef, input.ownerRouteId, input.goal.id, input.goal.nativeGoalId, input.goal.sessionId, input.runId, input.turn, input.acceptance.contractId, input.acceptance.receiptDigest, input.ownerRunTraceDigest, now, now);
|
|
5624
|
+
const recorded = this.insertWorkflowTraceRevisionInTransaction(revision);
|
|
5625
|
+
if (recorded.replayed) {
|
|
5626
|
+
throw new DeliveryStoreError('version-conflict', 'new owner-anchored workflow unexpectedly replayed a trace version');
|
|
5627
|
+
}
|
|
5628
|
+
return Object.freeze({ outcome: 'trace-recorded', revision, template, replayed: false });
|
|
5629
|
+
});
|
|
5630
|
+
}
|
|
5244
5631
|
insertVerifiedWorkflowTaskFeedback(input) {
|
|
5245
5632
|
if ((input.revision === undefined) !== (input.template === undefined)) {
|
|
5246
5633
|
throw new DeliveryStoreError('conflict', 'verified workflow trace and template must agree');
|
|
@@ -5317,7 +5704,7 @@ export class DeliveryStore {
|
|
|
5317
5704
|
SELECT * FROM workflow_template_registry WHERE template_ref = ?
|
|
5318
5705
|
`).get(template.templateRef);
|
|
5319
5706
|
if (row === undefined)
|
|
5320
|
-
return
|
|
5707
|
+
return this.getOwnerAnchoredWorkflowTemplate(template);
|
|
5321
5708
|
let contentValue;
|
|
5322
5709
|
let reviewValue;
|
|
5323
5710
|
try {
|
|
@@ -5438,6 +5825,7 @@ export class DeliveryStore {
|
|
|
5438
5825
|
return Object.freeze({
|
|
5439
5826
|
resolved,
|
|
5440
5827
|
review: Object.freeze({
|
|
5828
|
+
kind: 'inbox-anchored',
|
|
5441
5829
|
bindingId: row.owner_binding_id,
|
|
5442
5830
|
bindingVersion: review['bindingVersion'],
|
|
5443
5831
|
bindingGeneration: review['bindingGeneration'],
|
|
@@ -5450,6 +5838,131 @@ export class DeliveryStore {
|
|
|
5450
5838
|
version: row.version,
|
|
5451
5839
|
});
|
|
5452
5840
|
}
|
|
5841
|
+
/**
|
|
5842
|
+
* Rebuild a template learned from a Delivery-reverified owner-root Goal run.
|
|
5843
|
+
* Every column and receipt field is re-derived and cross-checked; a mismatch
|
|
5844
|
+
* fails closed rather than resolving attacker- or corruption-influenced data.
|
|
5845
|
+
*/
|
|
5846
|
+
getOwnerAnchoredWorkflowTemplate(template) {
|
|
5847
|
+
const row = this.database.prepare(`
|
|
5848
|
+
SELECT * FROM workflow_owner_anchored_templates WHERE template_ref = ?
|
|
5849
|
+
`).get(template.templateRef);
|
|
5850
|
+
if (row === undefined)
|
|
5851
|
+
return undefined;
|
|
5852
|
+
let contentValue;
|
|
5853
|
+
let reviewValue;
|
|
5854
|
+
try {
|
|
5855
|
+
contentValue = JSON.parse(row.content_json);
|
|
5856
|
+
reviewValue = JSON.parse(row.review_receipt_json);
|
|
5857
|
+
}
|
|
5858
|
+
catch {
|
|
5859
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow payload is corrupt');
|
|
5860
|
+
}
|
|
5861
|
+
if (typeof reviewValue !== 'object' || reviewValue === null || Array.isArray(reviewValue)) {
|
|
5862
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow review receipt is corrupt');
|
|
5863
|
+
}
|
|
5864
|
+
const review = reviewValue;
|
|
5865
|
+
const content = validateWorkflowAutomationTemplateContent(contentValue);
|
|
5866
|
+
const exactReviewKeys = [
|
|
5867
|
+
'contractVersion', 'kind', 'limitation', 'provenance', 'scheduleUnconfirmed', 'templateDigest',
|
|
5868
|
+
'bindingId', 'bindingVersion', 'bindingGeneration', 'principalId',
|
|
5869
|
+
'ownerRouteId', 'goalId', 'nativeGoalId', 'sessionId', 'runId', 'turn',
|
|
5870
|
+
'acceptanceContractId', 'acceptanceReceiptDigest', 'ownerRunTraceDigest', 'taskRef',
|
|
5871
|
+
].sort();
|
|
5872
|
+
const bindingVersion = review['bindingVersion'];
|
|
5873
|
+
const bindingGeneration = review['bindingGeneration'];
|
|
5874
|
+
const turn = review['turn'];
|
|
5875
|
+
if (Object.keys(review).sort().some((key, index) => key !== exactReviewKeys[index])
|
|
5876
|
+
|| Object.keys(review).length !== exactReviewKeys.length
|
|
5877
|
+
|| review['contractVersion'] !== 1
|
|
5878
|
+
|| review['kind'] !== 'owner-anchored-template-review'
|
|
5879
|
+
|| review['limitation'] !== 'deidentification-unproven'
|
|
5880
|
+
|| review['provenance'] !== 'owner-goal-success'
|
|
5881
|
+
|| review['scheduleUnconfirmed'] !== true
|
|
5882
|
+
|| review['templateDigest'] !== row.template_digest
|
|
5883
|
+
|| review['bindingId'] !== row.owner_binding_id
|
|
5884
|
+
|| review['principalId'] !== row.principal_id
|
|
5885
|
+
|| typeof bindingVersion !== 'number' || !Number.isSafeInteger(bindingVersion) || bindingVersion < 1
|
|
5886
|
+
|| typeof bindingGeneration !== 'number' || !Number.isSafeInteger(bindingGeneration) || bindingGeneration < 1
|
|
5887
|
+
|| bindingVersion !== row.binding_version || bindingGeneration !== row.binding_generation
|
|
5888
|
+
|| review['ownerRouteId'] !== row.owner_route_id
|
|
5889
|
+
|| review['goalId'] !== row.goal_id
|
|
5890
|
+
|| review['nativeGoalId'] !== row.native_goal_id
|
|
5891
|
+
|| review['sessionId'] !== row.session_id
|
|
5892
|
+
|| review['runId'] !== row.run_id
|
|
5893
|
+
|| review['turn'] !== row.turn
|
|
5894
|
+
|| typeof turn !== 'number' || !Number.isSafeInteger(turn) || turn < 0
|
|
5895
|
+
|| review['acceptanceContractId'] !== row.acceptance_contract_id
|
|
5896
|
+
|| review['acceptanceReceiptDigest'] !== row.acceptance_receipt_digest
|
|
5897
|
+
|| review['ownerRunTraceDigest'] !== row.owner_run_trace_digest
|
|
5898
|
+
|| review['taskRef'] !== row.task_ref
|
|
5899
|
+
|| typeof review['goalId'] !== 'string' || typeof review['nativeGoalId'] !== 'string'
|
|
5900
|
+
|| typeof review['sessionId'] !== 'string' || typeof review['runId'] !== 'string'
|
|
5901
|
+
|| typeof review['ownerRouteId'] !== 'string'
|
|
5902
|
+
|| typeof review['acceptanceContractId'] !== 'string'
|
|
5903
|
+
|| typeof review['acceptanceReceiptDigest'] !== 'string'
|
|
5904
|
+
|| !/^[a-f0-9]{64}$/u.test(review['acceptanceReceiptDigest'])
|
|
5905
|
+
|| !/^[a-f0-9]{64}$/u.test(review['ownerRunTraceDigest'])
|
|
5906
|
+
|| !/^[a-f0-9]{64}$/u.test(review['taskRef'])) {
|
|
5907
|
+
throw new DeliveryStoreError('conflict', 'owner-anchored workflow review receipt columns do not match');
|
|
5908
|
+
}
|
|
5909
|
+
const attestationDigest = growthObjectDigest({
|
|
5910
|
+
contract: 'assistant-delivery-workflow-owner-anchored-template-review/v1',
|
|
5911
|
+
receipt: review,
|
|
5912
|
+
});
|
|
5913
|
+
const storedTemplate = validateWorkflowAutomationTemplate({
|
|
5914
|
+
templateRef: row.template_ref,
|
|
5915
|
+
templateDigest: row.template_digest,
|
|
5916
|
+
privacyAttestation: {
|
|
5917
|
+
kind: 'owner-anchored',
|
|
5918
|
+
limitation: 'deidentification-unproven',
|
|
5919
|
+
provenance: 'owner-goal-success',
|
|
5920
|
+
attestationId: row.privacy_attestation_id,
|
|
5921
|
+
attestationDigest: row.privacy_attestation_digest,
|
|
5922
|
+
},
|
|
5923
|
+
});
|
|
5924
|
+
if (JSON.stringify(storedTemplate) !== JSON.stringify(template)
|
|
5925
|
+
|| attestationDigest !== row.privacy_attestation_digest
|
|
5926
|
+
|| storedTemplate.privacyAttestation.attestationId !== `workflow-owner-anchored:${attestationDigest}`) {
|
|
5927
|
+
throw new DeliveryStoreError('receipt-mismatch', 'owner-anchored workflow attestation is stale');
|
|
5928
|
+
}
|
|
5929
|
+
const contentBinding = this.getBinding(row.owner_binding_id);
|
|
5930
|
+
const resolved = validateResolvedWorkflowAutomationTemplate({
|
|
5931
|
+
contractVersion: 1,
|
|
5932
|
+
template: storedTemplate,
|
|
5933
|
+
...content,
|
|
5934
|
+
});
|
|
5935
|
+
if (workflowScopeKey(content.scope) !== row.scope_key
|
|
5936
|
+
|| content.scope.workspace !== row.workspace || content.scope.preset !== row.preset
|
|
5937
|
+
|| content.ownerBindingId !== row.owner_binding_id || content.deliveryBindingId !== row.owner_binding_id
|
|
5938
|
+
|| contentBinding === undefined
|
|
5939
|
+
|| content.principalId !== externalPrincipalId(contentBinding.principal)
|
|
5940
|
+
|| workflowAutomationTemplateContentDigest(content) !== row.template_digest) {
|
|
5941
|
+
throw new DeliveryStoreError('receipt-mismatch', 'owner-anchored workflow content columns do not match');
|
|
5942
|
+
}
|
|
5943
|
+
return Object.freeze({
|
|
5944
|
+
resolved,
|
|
5945
|
+
review: Object.freeze({
|
|
5946
|
+
kind: 'goal-anchored',
|
|
5947
|
+
bindingId: row.owner_binding_id,
|
|
5948
|
+
bindingVersion: row.binding_version,
|
|
5949
|
+
bindingGeneration: row.binding_generation,
|
|
5950
|
+
principalId: row.principal_id,
|
|
5951
|
+
ownerRouteId: row.owner_route_id,
|
|
5952
|
+
goalId: row.goal_id,
|
|
5953
|
+
nativeGoalId: row.native_goal_id,
|
|
5954
|
+
sessionId: row.session_id,
|
|
5955
|
+
runId: row.run_id,
|
|
5956
|
+
turn: row.turn,
|
|
5957
|
+
acceptanceContractId: row.acceptance_contract_id,
|
|
5958
|
+
acceptanceReceiptDigest: row.acceptance_receipt_digest,
|
|
5959
|
+
ownerRunTraceDigest: row.owner_run_trace_digest,
|
|
5960
|
+
taskRef: row.task_ref,
|
|
5961
|
+
}),
|
|
5962
|
+
status: row.status,
|
|
5963
|
+
version: row.version,
|
|
5964
|
+
});
|
|
5965
|
+
}
|
|
5453
5966
|
requeueCurrentWorkflowTraces() {
|
|
5454
5967
|
this.assertOpen();
|
|
5455
5968
|
const now = this.now();
|