@dsh-enhanced/assistant-delivery 0.1.48 → 0.1.49
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 +36 -18
- package/lib/agent-runtime.d.ts +17 -1
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +52 -39
- package/lib/agent-runtime.js.map +1 -1
- package/lib/feedback-command.d.ts +2 -0
- package/lib/feedback-command.d.ts.map +1 -1
- package/lib/feedback-command.js +15 -0
- package/lib/feedback-command.js.map +1 -1
- package/lib/foreground-observation.d.ts +13 -0
- package/lib/foreground-observation.d.ts.map +1 -1
- package/lib/foreground-observation.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/native-web-owner.js +3 -3
- package/lib/native-web-owner.js.map +1 -1
- package/lib/operator-snapshot.d.ts +1 -2
- package/lib/operator-snapshot.d.ts.map +1 -1
- package/lib/operator-snapshot.js +8 -3
- package/lib/operator-snapshot.js.map +1 -1
- package/lib/service.d.ts +54 -2
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +350 -26
- package/lib/service.js.map +1 -1
- package/lib/session-lease-runtime.js +1 -1
- package/lib/session-lease-runtime.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +113 -2
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +105 -11
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +490 -13
- package/lib/store.js.map +1 -1
- package/lib/types.d.ts +70 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +52 -47
package/lib/store.js
CHANGED
|
@@ -4,10 +4,26 @@ import { acceptanceCanonicalJson } from '@dsh-enhanced/task-acceptance-contract'
|
|
|
4
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
|
-
import { parseFeedbackCommand } from './feedback-command.js';
|
|
7
|
+
import { parseFeedbackCommand, parseNaturalObjectiveFeedback } from './feedback-command.js';
|
|
8
8
|
import { validateOwnerGoalOutcomeFeedbackLocator, validateOwnerGoalOutcomeFeedbackProof, } from './goal-wake-types.js';
|
|
9
9
|
import { openDeliveryDatabase } from './sqlite.js';
|
|
10
10
|
import { deriveDeterministicallyDeidentifiedWorkflowTemplate, getDeterministicallyDeidentifiedWorkflowTemplate, } from './workflow-auto-producer.js';
|
|
11
|
+
function foregroundSourceByteLimit(value) {
|
|
12
|
+
if (value === undefined)
|
|
13
|
+
return 16_384;
|
|
14
|
+
if (!Number.isSafeInteger(value) || value < 1 || value > 16_384) {
|
|
15
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source byte limit must be between 1 and 16384');
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function foregroundSourceText(text, limit) {
|
|
20
|
+
const bytes = Buffer.from(text, 'utf8');
|
|
21
|
+
let end = Math.min(bytes.length, limit);
|
|
22
|
+
while (end > 0 && end < bytes.length && (bytes[end] & 0xc0) === 0x80)
|
|
23
|
+
end--;
|
|
24
|
+
return Object.freeze({ text: bytes.subarray(0, end).toString('utf8'), fullTextDigest: createHash('sha256').update(bytes).digest('hex'),
|
|
25
|
+
returnedBytes: end, fullBytes: bytes.length, truncated: end < bytes.length });
|
|
26
|
+
}
|
|
11
27
|
export class DeliveryStoreError extends Error {
|
|
12
28
|
code;
|
|
13
29
|
constructor(code, message) {
|
|
@@ -1355,6 +1371,7 @@ export class DeliveryStore {
|
|
|
1355
1371
|
codeGenerator;
|
|
1356
1372
|
maxTextBytes;
|
|
1357
1373
|
closed = false;
|
|
1374
|
+
writerTransactionActive = false;
|
|
1358
1375
|
constructor(options) {
|
|
1359
1376
|
this.database = openDeliveryDatabase(options.path);
|
|
1360
1377
|
this.now = options.now ?? Date.now;
|
|
@@ -3150,6 +3167,32 @@ export class DeliveryStore {
|
|
|
3150
3167
|
completedAt: row.completed_at, executionRef: row.execution_ref, modelSelectionState: row.model_selection_state,
|
|
3151
3168
|
...(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
3169
|
}
|
|
3170
|
+
/** One SQL snapshot binds a pending execution to its still claimed Inbox and current owner. */
|
|
3171
|
+
inspectPendingForegroundToolTask(input) {
|
|
3172
|
+
this.assertOpen();
|
|
3173
|
+
const row = this.database.prepare(`SELECT execution.dispatched_at AS dispatched_at
|
|
3174
|
+
FROM delivery_foreground_executions AS execution
|
|
3175
|
+
JOIN inbox_messages AS inbox ON inbox.id = execution.inbox_id
|
|
3176
|
+
JOIN conversation_bindings AS binding ON binding.id = execution.binding_id
|
|
3177
|
+
JOIN delivery_principals AS principal ON principal.id = binding.principal_id
|
|
3178
|
+
WHERE execution.inbox_id = ? AND execution.status = 'pending' AND execution.completed_at IS NULL
|
|
3179
|
+
AND inbox.status = 'claimed' AND inbox.binding_id = execution.binding_id
|
|
3180
|
+
AND binding.status = 'active' AND binding.session_id = ?
|
|
3181
|
+
AND binding.id = ? AND binding.version = ? AND binding.generation = ?
|
|
3182
|
+
AND binding.workspace = ? AND binding.agent_preset = ?
|
|
3183
|
+
AND principal.status = 'active' AND principal.role = 'owner'
|
|
3184
|
+
AND principal.id = ? AND principal.version = ?
|
|
3185
|
+
AND execution.workspace = binding.workspace AND execution.preset = binding.agent_preset
|
|
3186
|
+
AND execution.principal_record_id = principal.id AND execution.principal_version = principal.version
|
|
3187
|
+
AND execution.binding_version = binding.version AND execution.binding_generation = binding.generation
|
|
3188
|
+
LIMIT 1`).get(input.inboxId, input.sessionId, input.binding.id, input.binding.version, input.binding.generation, input.scope.workspace, input.scope.preset, input.owner.principalRecordId, input.owner.principalVersion);
|
|
3189
|
+
if (row === undefined)
|
|
3190
|
+
return undefined;
|
|
3191
|
+
return Object.freeze({ protocol: 'assistant-delivery/foreground-task/v1',
|
|
3192
|
+
inboxId: input.inboxId, sessionId: input.sessionId,
|
|
3193
|
+
scope: Object.freeze({ ...input.scope }), owner: Object.freeze({ ...input.owner }),
|
|
3194
|
+
binding: Object.freeze({ ...input.binding }), dispatchedAt: row.dispatched_at });
|
|
3195
|
+
}
|
|
3153
3196
|
/** Immutable pre-prompt receipt for an authenticated owner foreground turn. */
|
|
3154
3197
|
bindForegroundTaskExecution(input) {
|
|
3155
3198
|
this.assertOpen();
|
|
@@ -3244,6 +3287,216 @@ export class DeliveryStore {
|
|
|
3244
3287
|
throw new DeliveryStoreError('conflict', 'foreground execution model selection state is invalid');
|
|
3245
3288
|
});
|
|
3246
3289
|
}
|
|
3290
|
+
foregroundSourceFence(input, authorityInput) {
|
|
3291
|
+
const authority = canonicalOwnerRouteAuthority(authorityInput);
|
|
3292
|
+
const binding = this.getActiveBinding(authority.conversation);
|
|
3293
|
+
const owner = binding === undefined ? undefined : this.getPrincipal(binding.principal);
|
|
3294
|
+
const authorityHash = ownerRouteAuthorityHash(authority);
|
|
3295
|
+
if (input.authorityId !== authority.id || input.principalId !== externalPrincipalId(authority.principal)
|
|
3296
|
+
|| input.workspace !== authority.workspace || input.agentPreset !== authority.agentPreset
|
|
3297
|
+
|| input.expectedOwner?.authorityHash !== authorityHash
|
|
3298
|
+
|| input.expectedOwner.principalRecordId !== owner?.id || input.expectedOwner.principalVersion !== owner?.version
|
|
3299
|
+
|| binding?.status !== 'active' || !bindingMatchesOwnerRoute(binding, authority)
|
|
3300
|
+
|| owner?.role !== 'owner' || owner.status !== 'active') {
|
|
3301
|
+
throw new DeliveryStoreError('unauthorized-principal', 'foreground source owner authority changed');
|
|
3302
|
+
}
|
|
3303
|
+
// The current binding generation intentionally does not enter this key: /new
|
|
3304
|
+
// retires a Session, not the stable owner's completion cursor.
|
|
3305
|
+
const scopeKey = digest(acceptanceCanonicalJson({ authorityId: authority.id, authorityHash,
|
|
3306
|
+
principalId: input.principalId, principalRecordId: owner.id, principalVersion: owner.version,
|
|
3307
|
+
workspace: authority.workspace, preset: authority.agentPreset }));
|
|
3308
|
+
return { authority, authorityHash, scopeKey };
|
|
3309
|
+
}
|
|
3310
|
+
foregroundSource(row, input, fence) {
|
|
3311
|
+
const inbox = this.getInbox(row.inbox_id);
|
|
3312
|
+
const binding = this.getBinding(row.binding_id);
|
|
3313
|
+
if (!inbox || !binding || inbox.bindingId !== row.binding_id
|
|
3314
|
+
|| row.workspace !== input.workspace || row.preset !== input.agentPreset
|
|
3315
|
+
|| row.principal_record_id !== input.expectedOwner.principalRecordId
|
|
3316
|
+
|| row.principal_version !== input.expectedOwner.principalVersion
|
|
3317
|
+
|| !bindingMatchesOwnerRoute(binding, fence.authority)
|
|
3318
|
+
|| binding.generation !== row.binding_generation
|
|
3319
|
+
|| !(binding.status === 'active' && binding.version === row.binding_version
|
|
3320
|
+
|| binding.status === 'revoked' && binding.version === row.binding_version + 1)
|
|
3321
|
+
|| inbox.admissionCursor.epoch !== this.databaseInstanceId
|
|
3322
|
+
|| row.completed_at === null || row.execution_ref !== inbox.id
|
|
3323
|
+
|| !Number.isSafeInteger(row.completion_sequence) || row.completion_sequence < 1)
|
|
3324
|
+
return undefined;
|
|
3325
|
+
try {
|
|
3326
|
+
const envelope = canonicalEnvelope(inbox.envelope, this.maxTextBytes);
|
|
3327
|
+
if (digest(JSON.stringify(envelope)) !== inbox.envelopeHash
|
|
3328
|
+
|| envelope.kind !== 'text' || parseDeliveryCommand({ ...envelope, kind: 'command' }) !== undefined
|
|
3329
|
+
|| JSON.stringify(envelope.principal) !== JSON.stringify(binding.principal)
|
|
3330
|
+
|| JSON.stringify(envelope.conversation) !== JSON.stringify(binding.conversation))
|
|
3331
|
+
return undefined;
|
|
3332
|
+
}
|
|
3333
|
+
catch {
|
|
3334
|
+
return undefined;
|
|
3335
|
+
}
|
|
3336
|
+
const execution = Object.freeze({ dispatchedAt: row.dispatched_at,
|
|
3337
|
+
status: row.status, quiescent: row.quiescent === 1, completedAt: row.completed_at,
|
|
3338
|
+
executionRef: row.execution_ref, modelSelectionState: row.model_selection_state,
|
|
3339
|
+
...(row.model_selection_state === 'frozen' && row.model_provider && row.model_id ? {
|
|
3340
|
+
modelSelection: Object.freeze({ provider: row.model_provider, model: row.model_id,
|
|
3341
|
+
...(row.model_reasoning_effort === null ? {} : { reasoningEffort: row.model_reasoning_effort }) }),
|
|
3342
|
+
} : {}) });
|
|
3343
|
+
const content = { protocol: 'assistant-delivery/owner-foreground-source/v1', inboxId: inbox.id,
|
|
3344
|
+
completionSequence: row.completion_sequence, admissionCursor: Object.freeze({ ...inbox.admissionCursor }),
|
|
3345
|
+
authorityId: fence.authority.id, authorityHash: fence.authorityHash, principalId: input.principalId,
|
|
3346
|
+
owner: Object.freeze({ principalRecordId: row.principal_record_id, principalVersion: row.principal_version }),
|
|
3347
|
+
scope: Object.freeze({ workspace: row.workspace, preset: row.preset }),
|
|
3348
|
+
binding: Object.freeze({ id: row.binding_id, version: row.binding_version,
|
|
3349
|
+
generation: row.binding_generation, sessionId: binding.sessionId }), execution };
|
|
3350
|
+
return Object.freeze({ ...content, sourceDigest: digest(acceptanceCanonicalJson({ ...content, envelopeHash: inbox.envelopeHash })) });
|
|
3351
|
+
}
|
|
3352
|
+
/** Host-only durable completion feed. Mutable delivery state never hides a completion. */
|
|
3353
|
+
listOwnerForegroundTaskSources(input, authority) {
|
|
3354
|
+
this.assertOpen();
|
|
3355
|
+
const limit = input.limit ?? 50;
|
|
3356
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
|
|
3357
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source page limit must be between 1 and 100');
|
|
3358
|
+
}
|
|
3359
|
+
return this.transaction(() => {
|
|
3360
|
+
const fence = this.foregroundSourceFence(input, authority);
|
|
3361
|
+
const watermark = this.database.prepare('SELECT current_sequence FROM delivery_foreground_completion_clock WHERE singleton = 1')
|
|
3362
|
+
.get().current_sequence;
|
|
3363
|
+
const after = input.after;
|
|
3364
|
+
if (after !== undefined && (after.protocol !== 'assistant-delivery/owner-foreground-source-cursor/v1'
|
|
3365
|
+
|| after.epoch !== this.databaseInstanceId || after.scopeKey !== fence.scopeKey
|
|
3366
|
+
|| !Number.isSafeInteger(after.sequence) || after.sequence < 0 || after.sequence > watermark)) {
|
|
3367
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source cursor is not bound to this owner and watermark');
|
|
3368
|
+
}
|
|
3369
|
+
const rows = this.database.prepare(`SELECT * FROM delivery_foreground_executions
|
|
3370
|
+
WHERE principal_record_id = ? AND principal_version = ? AND workspace = ? AND preset = ?
|
|
3371
|
+
AND status != 'pending' AND completion_sequence > ? AND completion_sequence <= ?
|
|
3372
|
+
ORDER BY completion_sequence LIMIT ?`).all(input.expectedOwner.principalRecordId, input.expectedOwner.principalVersion, input.workspace, input.agentPreset, after?.sequence ?? 0, watermark, limit + 1);
|
|
3373
|
+
const hasMore = rows.length > limit;
|
|
3374
|
+
const scanned = rows.slice(0, limit);
|
|
3375
|
+
const items = scanned.flatMap(row => {
|
|
3376
|
+
const source = this.foregroundSource(row, input, fence);
|
|
3377
|
+
return source === undefined ? [] : [source];
|
|
3378
|
+
});
|
|
3379
|
+
const nextCursor = Object.freeze({ protocol: 'assistant-delivery/owner-foreground-source-cursor/v1',
|
|
3380
|
+
epoch: this.databaseInstanceId, scopeKey: fence.scopeKey,
|
|
3381
|
+
sequence: hasMore ? scanned[scanned.length - 1].completion_sequence : watermark });
|
|
3382
|
+
return Object.freeze({ items: Object.freeze(items), nextCursor, watermark, hasMore });
|
|
3383
|
+
});
|
|
3384
|
+
}
|
|
3385
|
+
/** Host-only exact inbox metadata under the same owner writer fence as the feed. */
|
|
3386
|
+
inspectOwnerForegroundTaskSource(input, authority) {
|
|
3387
|
+
this.assertOpen();
|
|
3388
|
+
if (typeof input.inboxId !== 'string' || input.inboxId === '') {
|
|
3389
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source inbox ID is invalid');
|
|
3390
|
+
}
|
|
3391
|
+
const inspect = () => {
|
|
3392
|
+
const fence = this.foregroundSourceFence(input, authority);
|
|
3393
|
+
const row = this.database.prepare(`SELECT * FROM delivery_foreground_executions
|
|
3394
|
+
WHERE inbox_id = ? AND status != 'pending'`).get(input.inboxId);
|
|
3395
|
+
return row === undefined ? undefined : this.foregroundSource(row, input, fence);
|
|
3396
|
+
};
|
|
3397
|
+
// A route writer fence may inspect the same source while holding this
|
|
3398
|
+
// connection's transaction. Reuse that atomic snapshot and its write lock.
|
|
3399
|
+
return this.writerTransactionActive ? inspect() : this.transaction(inspect);
|
|
3400
|
+
}
|
|
3401
|
+
/** Content comes from the canonical Inbox/Outbox, with no alternate text ledger. */
|
|
3402
|
+
readOwnerForegroundTaskSource(input, authority) {
|
|
3403
|
+
this.assertOpen();
|
|
3404
|
+
const inputLimit = foregroundSourceByteLimit(input.maxInputBytes);
|
|
3405
|
+
const replyLimit = foregroundSourceByteLimit(input.maxReplyBytes);
|
|
3406
|
+
const read = () => {
|
|
3407
|
+
const fence = this.foregroundSourceFence(input, authority);
|
|
3408
|
+
return this.foregroundSourceContent(input, fence, inputLimit, replyLimit);
|
|
3409
|
+
};
|
|
3410
|
+
// A source fence already holds this connection's writer transaction. Reuse
|
|
3411
|
+
// its owner check and snapshot without starting a nested BEGIN.
|
|
3412
|
+
return this.writerTransactionActive ? read() : this.transaction(read);
|
|
3413
|
+
}
|
|
3414
|
+
/** Lock order: Delivery, then downstream Evaluation/Memory writer fences. */
|
|
3415
|
+
withOwnerForegroundTaskSourceFence(input, authority, callback) {
|
|
3416
|
+
if (typeof callback !== 'function')
|
|
3417
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source callback is invalid');
|
|
3418
|
+
return this.withForegroundSourceRead(input, authority, source => {
|
|
3419
|
+
if (source === undefined)
|
|
3420
|
+
throw new DeliveryStoreError('conflict', 'foreground source is unavailable or changed');
|
|
3421
|
+
const result = callback(source);
|
|
3422
|
+
this.assertForegroundSourceSyncResult(result);
|
|
3423
|
+
return result;
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3426
|
+
/** One owner check and one Delivery writer transaction for an ordered set of exact sources. */
|
|
3427
|
+
withOwnerForegroundTaskSourcesFence(input, authority, callback) {
|
|
3428
|
+
this.assertOpen();
|
|
3429
|
+
if (typeof callback !== 'function')
|
|
3430
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source callback is invalid');
|
|
3431
|
+
if (!Array.isArray(input.sources) || input.sources.length > 1_000) {
|
|
3432
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source batch must contain at most 1000 sources');
|
|
3433
|
+
}
|
|
3434
|
+
const inboxIds = new Set();
|
|
3435
|
+
for (const source of input.sources) {
|
|
3436
|
+
if (source === null || typeof source !== 'object' || typeof source.inboxId !== 'string'
|
|
3437
|
+
|| source.inboxId === '' || typeof source.expectedSourceDigest !== 'string'
|
|
3438
|
+
|| inboxIds.has(source.inboxId)) {
|
|
3439
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source batch requires unique inbox IDs and digests');
|
|
3440
|
+
}
|
|
3441
|
+
inboxIds.add(source.inboxId);
|
|
3442
|
+
}
|
|
3443
|
+
return this.transaction(() => {
|
|
3444
|
+
const fence = this.foregroundSourceFence(input, authority);
|
|
3445
|
+
const sources = Object.freeze(input.sources.map(source => this.foregroundSourceContent({ ...input, inboxId: source.inboxId, expectedSourceDigest: source.expectedSourceDigest }, fence, 16_384, 16_384)));
|
|
3446
|
+
const result = callback(sources);
|
|
3447
|
+
this.assertForegroundSourceSyncResult(result);
|
|
3448
|
+
return result;
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
assertForegroundSourceSyncResult(result) {
|
|
3452
|
+
if (result !== null && (typeof result === 'object' || typeof result === 'function') && 'then' in result) {
|
|
3453
|
+
// The native method consumes a real rejected Promise without invoking an
|
|
3454
|
+
// arbitrary thenable's getter or callback. The transaction then rolls back.
|
|
3455
|
+
try {
|
|
3456
|
+
Promise.prototype.then.call(result, undefined, () => undefined);
|
|
3457
|
+
}
|
|
3458
|
+
catch { /* Not a native Promise. */ }
|
|
3459
|
+
throw new DeliveryStoreError('invalid-binding', 'foreground source callback must be synchronous');
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
withForegroundSourceRead(input, authority, callback) {
|
|
3463
|
+
this.assertOpen();
|
|
3464
|
+
const inputLimit = foregroundSourceByteLimit(input.maxInputBytes);
|
|
3465
|
+
const replyLimit = foregroundSourceByteLimit(input.maxReplyBytes);
|
|
3466
|
+
return this.transaction(() => {
|
|
3467
|
+
const fence = this.foregroundSourceFence(input, authority);
|
|
3468
|
+
return callback(this.foregroundSourceContent(input, fence, inputLimit, replyLimit));
|
|
3469
|
+
});
|
|
3470
|
+
}
|
|
3471
|
+
foregroundSourceContent(input, fence, inputLimit, replyLimit) {
|
|
3472
|
+
const row = this.database.prepare(`SELECT * FROM delivery_foreground_executions
|
|
3473
|
+
WHERE inbox_id = ? AND status != 'pending'`).get(input.inboxId);
|
|
3474
|
+
const source = row === undefined ? undefined : this.foregroundSource(row, input, fence);
|
|
3475
|
+
if (!source || source.sourceDigest !== input.expectedSourceDigest || source.execution.status !== 'succeeded'
|
|
3476
|
+
|| !source.execution.quiescent || source.execution.modelSelectionState !== 'frozen'
|
|
3477
|
+
|| source.execution.modelSelection === undefined)
|
|
3478
|
+
return undefined;
|
|
3479
|
+
const inbox = this.getInbox(source.inboxId);
|
|
3480
|
+
const binding = this.getBinding(source.binding.id);
|
|
3481
|
+
const outbox = this.getOutboxByIdempotencyKey(`inbound:${inbox.id}:reply`);
|
|
3482
|
+
if (!outbox || outbox.intentHash !== digest(JSON.stringify(outbox.intent)))
|
|
3483
|
+
return undefined;
|
|
3484
|
+
const intent = outbox.intent;
|
|
3485
|
+
if (intent.bindingId !== binding.id || intent.replyToEventId !== inbox.envelope.eventId
|
|
3486
|
+
|| intent.idempotencyKey !== `inbound:${inbox.id}:reply`
|
|
3487
|
+
|| JSON.stringify(intent.target.principal) !== JSON.stringify(binding.principal)
|
|
3488
|
+
|| JSON.stringify(intent.target.conversation) !== JSON.stringify(binding.conversation)
|
|
3489
|
+
|| ![undefined, 'plain', 'markdown'].includes(intent.format)
|
|
3490
|
+
|| intent.approval !== undefined || intent.modelPicker !== undefined || intent.permissionPicker !== undefined
|
|
3491
|
+
|| Object.keys(intent.metadata ?? {}).length !== 0)
|
|
3492
|
+
return undefined;
|
|
3493
|
+
const inputText = foregroundSourceText(inbox.envelope.text, inputLimit);
|
|
3494
|
+
const reply = Object.freeze({ ...foregroundSourceText(intent.text, replyLimit), outboxId: outbox.id, intentDigest: outbox.intentHash });
|
|
3495
|
+
const contentDigest = digest(acceptanceCanonicalJson({ protocol: 'assistant-delivery/owner-foreground-source-content/v1',
|
|
3496
|
+
sourceDigest: source.sourceDigest, inputDigest: inputText.fullTextDigest,
|
|
3497
|
+
replyDigest: reply.fullTextDigest, outboxId: reply.outboxId, intentDigest: reply.intentDigest }));
|
|
3498
|
+
return Object.freeze({ source, sourceDigest: source.sourceDigest, contentDigest, input: inputText, reply });
|
|
3499
|
+
}
|
|
3247
3500
|
inspectForegroundExecutionForOwner(input) {
|
|
3248
3501
|
const row = this.database.prepare(`SELECT execution.dispatched_at, execution.status, execution.quiescent, execution.completed_at, execution.execution_ref,
|
|
3249
3502
|
execution.model_selection_state, execution.model_provider, execution.model_id, execution.model_reasoning_effort,
|
|
@@ -5095,16 +5348,220 @@ export class DeliveryStore {
|
|
|
5095
5348
|
return Object.freeze({ revision, ...(template === undefined ? {} : { template }), replayed: false });
|
|
5096
5349
|
});
|
|
5097
5350
|
}
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5351
|
+
getNaturalObjectiveIntent(inboxId) {
|
|
5352
|
+
this.assertOpen();
|
|
5353
|
+
const row = this.database.prepare('SELECT * FROM delivery_natural_objective_intents WHERE inbox_id = ?')
|
|
5354
|
+
.get(inboxId);
|
|
5355
|
+
if (row === undefined)
|
|
5356
|
+
return undefined;
|
|
5357
|
+
return Object.freeze({
|
|
5358
|
+
inboxId: String(row['inbox_id']), envelopeHash: String(row['envelope_hash']),
|
|
5359
|
+
sourceInboxId: String(row['source_inbox_id']), sourceOutboxId: String(row['source_outbox_id']),
|
|
5360
|
+
bindingId: String(row['binding_id']), bindingVersion: Number(row['binding_version']),
|
|
5361
|
+
bindingGeneration: Number(row['binding_generation']), principalRecordId: String(row['principal_record_id']),
|
|
5362
|
+
principalVersion: Number(row['principal_version']), admissionSequence: Number(row['admission_sequence']),
|
|
5363
|
+
admissionEpoch: String(row['admission_epoch']),
|
|
5364
|
+
occurredAt: Number(row['occurred_at']), selection: row['selection'],
|
|
5365
|
+
status: row['intent_status'],
|
|
5366
|
+
...(row['command_json'] === null ? {} : { command: JSON.parse(String(row['command_json'])) }),
|
|
5367
|
+
...(row['canonical_json'] === null ? {} : { canonical: JSON.parse(String(row['canonical_json'])) }),
|
|
5368
|
+
});
|
|
5369
|
+
}
|
|
5370
|
+
listPendingNaturalObjectiveIntents(limit = 100, afterAdmissionSequence = 0) {
|
|
5371
|
+
this.assertOpen();
|
|
5372
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100)
|
|
5373
|
+
throw new DeliveryStoreError('conflict', 'invalid natural feedback limit');
|
|
5374
|
+
if (!Number.isSafeInteger(afterAdmissionSequence) || afterAdmissionSequence < 0)
|
|
5375
|
+
throw new DeliveryStoreError('conflict', 'invalid natural feedback cursor');
|
|
5376
|
+
const ids = this.database.prepare(`SELECT inbox_id FROM delivery_natural_objective_intents
|
|
5377
|
+
WHERE intent_status IN ('awaiting-evaluation', 'frozen') AND admission_sequence > ?
|
|
5378
|
+
ORDER BY admission_sequence LIMIT ?`)
|
|
5379
|
+
.all(afterAdmissionSequence, limit);
|
|
5380
|
+
return ids.map(row => this.getNaturalObjectiveIntent(row.inbox_id));
|
|
5381
|
+
}
|
|
5382
|
+
hasEarlierPendingNaturalObjectiveIntent(sourceOutboxId, admissionSequence) {
|
|
5383
|
+
this.assertOpen();
|
|
5384
|
+
return this.database.prepare(`SELECT 1 FROM delivery_natural_objective_intents
|
|
5385
|
+
WHERE source_outbox_id = ? AND admission_sequence < ?
|
|
5386
|
+
AND intent_status IN ('awaiting-evaluation', 'frozen') LIMIT 1`)
|
|
5387
|
+
.get(sourceOutboxId, admissionSequence) !== undefined;
|
|
5388
|
+
}
|
|
5389
|
+
/** Same source proof required by the local workflow commit, before Evaluation append. */
|
|
5390
|
+
isVerifiedForegroundObjectiveTarget(binding, sourceInboxId, sourceOutboxId) {
|
|
5391
|
+
this.assertOpen();
|
|
5392
|
+
const source = this.getInbox(sourceInboxId);
|
|
5393
|
+
const outbox = this.getOutbox(sourceOutboxId);
|
|
5394
|
+
return source?.status === 'processed' && source.bindingId === binding.id
|
|
5395
|
+
&& source.envelope.kind === 'text' && (source.envelope.attachments?.length ?? 0) === 0
|
|
5396
|
+
&& outbox !== undefined && outbox.intent.bindingId === binding.id
|
|
5397
|
+
&& outbox.intent.idempotencyKey === `inbound:${source.id}:reply`
|
|
5398
|
+
&& outbox.intent.replyToEventId === source.eventId
|
|
5399
|
+
&& ['accepted', 'delivered', 'read'].includes(outbox.status)
|
|
5400
|
+
&& outbox.providerMessageId !== undefined
|
|
5401
|
+
&& (outbox.intent.format === 'markdown' || outbox.intent.format === 'plain')
|
|
5402
|
+
&& outbox.intent.metadata === undefined && outbox.intent.modelPicker === undefined
|
|
5403
|
+
&& outbox.intent.permissionPicker === undefined && outbox.intent.approval === undefined
|
|
5404
|
+
&& this.getGoalOutcomeTarget(outbox.id) === undefined
|
|
5405
|
+
&& JSON.stringify(source.envelope.conversation) === JSON.stringify(binding.conversation)
|
|
5406
|
+
&& JSON.stringify(source.envelope.principal) === JSON.stringify(binding.principal)
|
|
5407
|
+
&& JSON.stringify(outbox.intent.target.conversation) === JSON.stringify(binding.conversation)
|
|
5408
|
+
&& JSON.stringify(outbox.intent.target.principal) === JSON.stringify(binding.principal);
|
|
5409
|
+
}
|
|
5410
|
+
/** Freeze the immutable owner wording and exact completed result before any Evaluation I/O. */
|
|
5411
|
+
prepareNaturalObjectiveIntent(input) {
|
|
5412
|
+
this.assertOpen();
|
|
5413
|
+
return this.transaction(() => {
|
|
5414
|
+
const binding = this.getBinding(input.binding.id);
|
|
5415
|
+
const owner = binding === undefined ? undefined : this.getPrincipal(binding.principal);
|
|
5416
|
+
const inbox = this.getInbox(input.feedbackInboxId);
|
|
5417
|
+
const source = this.getInbox(input.sourceInboxId);
|
|
5418
|
+
const outbox = this.getOutbox(input.sourceOutboxId);
|
|
5419
|
+
const existing = this.getNaturalObjectiveIntent(input.feedbackInboxId);
|
|
5420
|
+
if (binding?.status !== 'active' || binding.version !== input.binding.version
|
|
5421
|
+
|| binding.generation !== input.binding.generation || binding.sessionId !== input.binding.sessionId
|
|
5422
|
+
|| binding.conversation.kind !== 'dm' || owner?.status !== 'active' || owner.role !== 'owner'
|
|
5423
|
+
|| inbox?.status !== 'claimed' || inbox.bindingId !== binding.id || inbox.envelope.kind !== 'text'
|
|
5424
|
+
|| (inbox.envelope.attachments?.length ?? 0) !== 0
|
|
5425
|
+
|| parseNaturalObjectiveFeedback(inbox.envelope.text) !== input.selection
|
|
5426
|
+
|| !this.isVerifiedForegroundObjectiveTarget(binding, input.sourceInboxId, input.sourceOutboxId)
|
|
5427
|
+
|| source === undefined || outbox === undefined
|
|
5428
|
+
|| inbox.envelope.metadata?.replyToProviderMessageId !== outbox.providerMessageId
|
|
5429
|
+
|| JSON.stringify(inbox.envelope.conversation) !== JSON.stringify(binding.conversation)
|
|
5430
|
+
|| JSON.stringify(inbox.envelope.principal) !== JSON.stringify(binding.principal)) {
|
|
5431
|
+
throw new DeliveryStoreError('conflict', 'natural feedback does not target one completed owner turn');
|
|
5432
|
+
}
|
|
5433
|
+
if (existing !== undefined) {
|
|
5434
|
+
if (existing.envelopeHash !== inbox.envelopeHash || existing.sourceInboxId !== source.id
|
|
5435
|
+
|| existing.sourceOutboxId !== outbox.id || existing.selection !== input.selection
|
|
5436
|
+
|| existing.principalRecordId !== owner.id || existing.principalVersion !== owner.version
|
|
5437
|
+
|| existing.bindingVersion !== binding.version || existing.bindingGeneration !== binding.generation
|
|
5438
|
+
|| existing.admissionEpoch !== inbox.admissionCursor.epoch
|
|
5439
|
+
|| existing.admissionSequence !== inbox.admissionCursor.sequence) {
|
|
5440
|
+
throw new DeliveryStoreError('idempotency-conflict', 'natural feedback identity changed');
|
|
5441
|
+
}
|
|
5442
|
+
return existing;
|
|
5443
|
+
}
|
|
5444
|
+
const now = this.now();
|
|
5445
|
+
this.database.prepare(`INSERT INTO delivery_natural_objective_intents(
|
|
5446
|
+
inbox_id, envelope_hash, source_inbox_id, source_outbox_id, binding_id, binding_version,
|
|
5447
|
+
binding_generation, principal_record_id, principal_version, admission_epoch, admission_sequence,
|
|
5448
|
+
occurred_at, parser_version, intent_status, selection, command_json, canonical_json, created_at, updated_at
|
|
5449
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 'awaiting-evaluation', ?, NULL, NULL, ?, ?)`).run(inbox.id, inbox.envelopeHash, source.id, outbox.id, binding.id, binding.version, binding.generation, owner.id, owner.version, inbox.admissionCursor.epoch, inbox.admissionCursor.sequence, inbox.envelope.occurredAt, input.selection, now, now);
|
|
5450
|
+
return this.getNaturalObjectiveIntent(inbox.id);
|
|
5451
|
+
});
|
|
5452
|
+
}
|
|
5453
|
+
/** A CAS command is fixed once; retries may never inspect a newer version and rebase it. */
|
|
5454
|
+
freezeNaturalObjectiveIntent(inboxId, current) {
|
|
5455
|
+
this.assertOpen();
|
|
5456
|
+
return this.transaction(() => {
|
|
5457
|
+
const intent = this.getNaturalObjectiveIntent(inboxId);
|
|
5458
|
+
if (intent === undefined)
|
|
5459
|
+
return undefined;
|
|
5460
|
+
if (intent.status !== 'awaiting-evaluation')
|
|
5461
|
+
return intent;
|
|
5462
|
+
const earlier = this.database.prepare(`SELECT 1 FROM delivery_natural_objective_intents
|
|
5463
|
+
WHERE source_outbox_id = ? AND admission_sequence < ?
|
|
5464
|
+
AND intent_status IN ('awaiting-evaluation', 'frozen') LIMIT 1`)
|
|
5465
|
+
.get(intent.sourceOutboxId, intent.admissionSequence);
|
|
5466
|
+
if (earlier !== undefined)
|
|
5467
|
+
return undefined;
|
|
5468
|
+
const later = this.database.prepare(`SELECT 1 FROM delivery_natural_objective_intents
|
|
5469
|
+
WHERE source_outbox_id = ? AND admission_sequence > ?
|
|
5470
|
+
AND intent_status = 'projected' LIMIT 1`)
|
|
5471
|
+
.get(intent.sourceOutboxId, intent.admissionSequence);
|
|
5472
|
+
if (later !== undefined) {
|
|
5473
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents SET intent_status = 'conflict', updated_at = ? WHERE inbox_id = ?`)
|
|
5474
|
+
.run(this.now(), inboxId);
|
|
5475
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5476
|
+
}
|
|
5477
|
+
const latest = this.database.prepare(`SELECT inbox.envelope_json, admission.admission_sequence
|
|
5478
|
+
FROM delivery_owner_objective_commands AS command
|
|
5479
|
+
JOIN inbox_messages AS inbox ON inbox.id = command.inbox_id
|
|
5480
|
+
JOIN delivery_inbox_admissions AS admission ON admission.inbox_id = inbox.id
|
|
5481
|
+
WHERE command.source_outbox_id = ? AND json_extract(command.result_json, '$.error') IS NULL
|
|
5482
|
+
ORDER BY command.version DESC, admission.admission_sequence DESC LIMIT 1`)
|
|
5483
|
+
.get(intent.sourceOutboxId);
|
|
5484
|
+
if (latest !== undefined) {
|
|
5485
|
+
const previous = JSON.parse(latest.envelope_json);
|
|
5486
|
+
if (latest.admission_sequence >= intent.admissionSequence
|
|
5487
|
+
|| previous.occurredAt >= intent.occurredAt) {
|
|
5488
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents SET intent_status = 'conflict', updated_at = ? WHERE inbox_id = ?`)
|
|
5489
|
+
.run(this.now(), inboxId);
|
|
5490
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5491
|
+
}
|
|
5492
|
+
}
|
|
5493
|
+
const projected = this.database.prepare(`SELECT MAX(occurred_at) AS occurred_at
|
|
5494
|
+
FROM delivery_natural_objective_intents WHERE source_outbox_id = ?
|
|
5495
|
+
AND admission_sequence < ? AND intent_status = 'projected'`)
|
|
5496
|
+
.get(intent.sourceOutboxId, intent.admissionSequence);
|
|
5497
|
+
if (projected.occurred_at !== null && projected.occurred_at >= intent.occurredAt) {
|
|
5498
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents SET intent_status = 'conflict', updated_at = ? WHERE inbox_id = ?`)
|
|
5499
|
+
.run(this.now(), inboxId);
|
|
5500
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5501
|
+
}
|
|
5502
|
+
const local = this.verifiedWorkflowObjectiveState(intent.sourceOutboxId, { principalRecordId: intent.principalRecordId, principalVersion: intent.principalVersion });
|
|
5503
|
+
if (local !== undefined && (current === undefined || local.version !== current.version
|
|
5504
|
+
|| local.objectiveStatus !== current.objectiveStatus))
|
|
5505
|
+
return undefined;
|
|
5506
|
+
if (current !== undefined && local === undefined)
|
|
5507
|
+
return undefined;
|
|
5508
|
+
if (intent.selection === 'withdraw' && (current === undefined || current.objectiveStatus === 'unknown')) {
|
|
5509
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents SET intent_status = 'conflict', updated_at = ? WHERE inbox_id = ?`)
|
|
5510
|
+
.run(this.now(), inboxId);
|
|
5511
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5512
|
+
}
|
|
5513
|
+
if (current !== undefined && current.objectiveStatus === intent.selection) {
|
|
5514
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents SET intent_status = 'projected', updated_at = ? WHERE inbox_id = ?`)
|
|
5515
|
+
.run(this.now(), inboxId);
|
|
5516
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5517
|
+
}
|
|
5518
|
+
const command = current === undefined
|
|
5519
|
+
? { kind: 'objective', objectiveStatus: intent.selection }
|
|
5520
|
+
: { kind: 'objective-revision', action: intent.selection === 'withdraw' ? 'withdraw' : 'correct',
|
|
5521
|
+
expectedVersion: current.version, previousStatus: current.objectiveStatus,
|
|
5522
|
+
objectiveStatus: intent.selection === 'withdraw' ? 'unknown' : intent.selection };
|
|
5523
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents
|
|
5524
|
+
SET intent_status = 'frozen', command_json = ?, updated_at = ? WHERE inbox_id = ?`)
|
|
5525
|
+
.run(JSON.stringify(command), this.now(), inboxId);
|
|
5526
|
+
return this.getNaturalObjectiveIntent(inboxId);
|
|
5527
|
+
});
|
|
5528
|
+
}
|
|
5529
|
+
settleNaturalObjectiveIntent(inboxId, status, canonical) {
|
|
5530
|
+
this.assertOpen();
|
|
5531
|
+
this.transaction(() => {
|
|
5532
|
+
const intent = this.getNaturalObjectiveIntent(inboxId);
|
|
5533
|
+
if (intent?.status !== 'frozen' && intent?.status !== status
|
|
5534
|
+
&& !(intent?.status === 'awaiting-evaluation' && status === 'conflict')) {
|
|
5535
|
+
throw new DeliveryStoreError('conflict', 'natural intent was not frozen');
|
|
5536
|
+
}
|
|
5537
|
+
if (intent.status === 'projected' && status !== 'projected')
|
|
5538
|
+
throw new DeliveryStoreError('conflict', 'projected natural feedback cannot regress');
|
|
5539
|
+
this.database.prepare(`UPDATE delivery_natural_objective_intents
|
|
5540
|
+
SET intent_status = ?, canonical_json = COALESCE(canonical_json, ?), updated_at = ? WHERE inbox_id = ?`)
|
|
5541
|
+
.run(status, canonical === undefined ? null : JSON.stringify(canonical), this.now(), inboxId);
|
|
5542
|
+
});
|
|
5543
|
+
}
|
|
5544
|
+
findNaturalObjectiveFeedbackByOperation(sourceOutboxId, operationId, lineage) {
|
|
5545
|
+
this.assertOpen();
|
|
5546
|
+
const row = this.database.prepare(`SELECT intent.inbox_id, intent.envelope_hash, intent.principal_record_id,
|
|
5547
|
+
intent.principal_version, inbox.envelope_json, inbox.envelope_hash AS current_hash
|
|
5548
|
+
FROM delivery_natural_objective_intents AS intent JOIN inbox_messages AS inbox ON inbox.id = intent.inbox_id
|
|
5549
|
+
WHERE intent.source_outbox_id = ? AND intent.principal_record_id = ? AND intent.principal_version = ?
|
|
5550
|
+
AND intent.command_json IS NOT NULL
|
|
5551
|
+
AND intent.intent_status IN ('frozen', 'projected')`).all(sourceOutboxId, lineage.principalRecordId, lineage.principalVersion);
|
|
5552
|
+
for (const candidate of row) {
|
|
5553
|
+
if (candidate.envelope_hash !== candidate.current_hash)
|
|
5554
|
+
continue;
|
|
5555
|
+
const derived = `owner-feedback:${createHash('sha256').update(JSON.stringify([candidate.inbox_id, candidate.envelope_hash])).digest('hex')}`;
|
|
5556
|
+
if (derived !== operationId)
|
|
5557
|
+
continue;
|
|
5558
|
+
const envelope = JSON.parse(candidate.envelope_json);
|
|
5559
|
+
if (envelope.kind !== 'text' || parseNaturalObjectiveFeedback(envelope.text) === undefined)
|
|
5560
|
+
continue;
|
|
5561
|
+
return Object.freeze({ inboxId: candidate.inbox_id, text: envelope.text.slice(0, 4096), truncated: envelope.text.length > 4096 });
|
|
5562
|
+
}
|
|
5563
|
+
return undefined;
|
|
5564
|
+
}
|
|
5108
5565
|
verifiedWorkflowObjectiveState(sourceOutboxId, lineage) {
|
|
5109
5566
|
const row = this.database.prepare(`SELECT version, objective_status, principal_record_id, principal_version FROM delivery_owner_objective_commands
|
|
5110
5567
|
WHERE source_outbox_id = ? AND json_extract(result_json, '$.error') IS NULL ORDER BY version DESC LIMIT 1`).get(sourceOutboxId);
|
|
@@ -5121,6 +5578,7 @@ export class DeliveryStore {
|
|
|
5121
5578
|
return undefined;
|
|
5122
5579
|
return initial === undefined ? undefined : { version: 1, objectiveStatus: initial.objective_status };
|
|
5123
5580
|
}
|
|
5581
|
+
/** Commit exact owner feedback (typed command or frozen natural intent) and closed-set workflow evidence. */
|
|
5124
5582
|
commitVerifiedWorkflowTraceFeedback(input) {
|
|
5125
5583
|
this.assertOpen();
|
|
5126
5584
|
if (typeof input !== 'object' || input === null
|
|
@@ -5147,13 +5605,28 @@ export class DeliveryStore {
|
|
|
5147
5605
|
const feedbackInbox = this.getInbox(feedbackInboxId);
|
|
5148
5606
|
const sourceInbox = this.getInbox(sourceInboxId);
|
|
5149
5607
|
const sourceOutbox = this.getOutbox(sourceOutboxId);
|
|
5608
|
+
const naturalIntent = this.getNaturalObjectiveIntent(feedbackInboxId);
|
|
5150
5609
|
const feedbackCommand = feedbackInbox === undefined || feedbackInbox.envelope.kind !== 'command'
|
|
5151
5610
|
? undefined
|
|
5152
5611
|
: parseDeliveryCommand(feedbackInbox.envelope);
|
|
5153
|
-
const
|
|
5612
|
+
const typedFeedback = feedbackCommand?.name !== 'feedback'
|
|
5154
5613
|
? undefined
|
|
5155
5614
|
: parseFeedbackCommand(feedbackCommand.rawInput);
|
|
5156
|
-
|
|
5615
|
+
const feedback = naturalIntent?.command ?? typedFeedback;
|
|
5616
|
+
const naturalMatches = naturalIntent === undefined || (feedbackInbox !== undefined
|
|
5617
|
+
&& feedbackInbox.envelope.kind === 'text'
|
|
5618
|
+
&& naturalIntent.status !== 'awaiting-evaluation' && naturalIntent.status !== 'conflict'
|
|
5619
|
+
&& naturalIntent.envelopeHash === feedbackInbox.envelopeHash
|
|
5620
|
+
&& naturalIntent.sourceInboxId === sourceInboxId && naturalIntent.sourceOutboxId === sourceOutboxId
|
|
5621
|
+
&& naturalIntent.bindingId === binding.id
|
|
5622
|
+
&& naturalIntent.bindingVersion === binding.version && naturalIntent.bindingGeneration === binding.generation
|
|
5623
|
+
&& naturalIntent.principalRecordId === owner.id && naturalIntent.principalVersion === owner.version
|
|
5624
|
+
&& naturalIntent.admissionEpoch === feedbackInbox.admissionCursor.epoch
|
|
5625
|
+
&& naturalIntent.admissionSequence === feedbackInbox.admissionCursor.sequence
|
|
5626
|
+
&& parseNaturalObjectiveFeedback(feedbackInbox.envelope.text) === naturalIntent.selection);
|
|
5627
|
+
if ((feedbackInbox?.status !== 'claimed' && !(naturalIntent !== undefined
|
|
5628
|
+
&& (feedbackInbox?.status === 'processed' || feedbackInbox?.status === 'dead_letter')))
|
|
5629
|
+
|| feedbackInbox?.bindingId !== binding.id || !naturalMatches
|
|
5157
5630
|
|| (feedbackInbox.envelope.attachments?.length ?? 0) !== 0
|
|
5158
5631
|
|| (feedback?.kind !== 'objective' && feedback?.kind !== 'objective-revision')
|
|
5159
5632
|
|| feedback.objectiveStatus !== input.objectiveStatus
|
|
@@ -6757,6 +7230,7 @@ export class DeliveryStore {
|
|
|
6757
7230
|
}
|
|
6758
7231
|
transaction(operation) {
|
|
6759
7232
|
this.database.exec('BEGIN IMMEDIATE');
|
|
7233
|
+
this.writerTransactionActive = true;
|
|
6760
7234
|
try {
|
|
6761
7235
|
const result = operation();
|
|
6762
7236
|
this.database.exec('COMMIT');
|
|
@@ -6766,6 +7240,9 @@ export class DeliveryStore {
|
|
|
6766
7240
|
this.database.exec('ROLLBACK');
|
|
6767
7241
|
throw error;
|
|
6768
7242
|
}
|
|
7243
|
+
finally {
|
|
7244
|
+
this.writerTransactionActive = false;
|
|
7245
|
+
}
|
|
6769
7246
|
}
|
|
6770
7247
|
advanceModelCommandEpoch(conversation) {
|
|
6771
7248
|
const hash = conversationHash(conversation);
|