@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/lib/service.js CHANGED
@@ -4,16 +4,16 @@ import { createHash, randomUUID } from 'node:crypto';
4
4
  import { Context, Service } from '@deepseek-ai/cordis';
5
5
  import Schema from '@deepseek-ai/schemastery';
6
6
  import { acceptanceCanonicalJson, goalDefinitionSituation } from '@dsh-enhanced/task-acceptance-contract';
7
- import { approvalReviewerOf, isAutoReviewEscalation, } from '@dsh-enhanced/assistant-policy';
7
+ import { approvalReviewerOf, } from '@dsh-enhanced/assistant-policy';
8
8
  // Cross-package wire literal, kept locally so this bundle can load without the
9
9
  // optional assistant-evaluation peer installed (a value import would defeat
10
10
  // peerDependenciesMeta.optional at module link time). Must stay identical to
11
11
  // TRUSTED_EVALUATION_PRODUCER_PROTOCOL in assistant-evaluation.
12
12
  const TRUSTED_EVALUATION_PRODUCER_PROTOCOL = 'assistant-evaluation/trusted-producer/v1';
13
- import { ASSISTANT_GROWTH_CONTRACT_VERSION, growthObjectDigest, validateResolvedWorkflowAutomationTemplate, validateWorkflowAutomationTemplate, validateWorkflowScope, validateWorkflowTraceProjectionReceipt, workflowArgumentShapeDigest, } from '@dsh-enhanced/assistant-growth-contract';
13
+ import { ASSISTANT_GROWTH_CONTRACT_VERSION, growthObjectDigest, isSourceOwnerContinuation, validateResolvedWorkflowAutomationTemplate, validateWorkflowAutomationTemplate, validateWorkflowScope, validateWorkflowTraceProjectionReceipt, workflowArgumentShapeDigest, } from '@dsh-enhanced/assistant-growth-contract';
14
14
  import { DeliveryAdapterRegistry, DeliveryCoordinator, InboundCoordinator, } from './coordinator.js';
15
15
  import { DeliveryStore, DeliveryStoreError, } from './store.js';
16
- import { DshDeliveryRuntime } from './agent-runtime.js';
16
+ import { DshDeliveryRuntime, redactOwnerLearningReply } from './agent-runtime.js';
17
17
  import { DeliverySessionLeases } from './session-lease-runtime.js';
18
18
  import { NativeWebOwner } from './native-web-owner.js';
19
19
  import { validateOwnerGoalOutcomeFeedbackLocator, validateOwnerGoalOutcomeFeedbackProof, } from './goal-wake-types.js';
@@ -24,7 +24,7 @@ import { bindingMatchesOwnerRoute, canonicalBackgroundSourceId as canonicalHostB
24
24
  import { isExactDeliveryCommand, parseDeliveryCommand } from './session-commands.js';
25
25
  import { deliveryT1PreferenceKeys, deliveryT1PreferenceValues } from './learning-command.js';
26
26
  import { assertReplyReplayAllowed, blockAgentRepliesForReplay as createReplyReplayBlock, } from './reply-replay.js';
27
- import { feedbackSignalInput, parseFeedbackCommand, classifyNaturalPreferenceDirective, observedResponseLanguage, } from './feedback-command.js';
27
+ import { feedbackSignalInput, parseFeedbackCommand, parseNaturalObjectiveFeedback, classifyNaturalPreferenceDirective, observedResponseLanguage, } from './feedback-command.js';
28
28
  import { DELIVERY_PREFERENCE_PROJECTION_PROTOCOL, TRUSTED_DELIVERY_PRESENTATION_PRODUCER_PROTOCOL, } from './types.js';
29
29
  const trustedDeliveryPreferenceProducers = new WeakSet();
30
30
  const deliveryPreferenceProducerProbe = Symbol('assistant-delivery.preference-producer-probe');
@@ -491,7 +491,7 @@ function exactToolApprovalCall(events, openTurn, callId, toolName) {
491
491
  callId, event.data.name, event.data.arguments],
492
492
  });
493
493
  }
494
- if (event.type === 'tool/code-dispatch-start' && String(event.data.subCallId) === callId) {
494
+ if (event.type === 'tool/ptc-dispatch-start' && String(event.data.subCallId) === callId) {
495
495
  const rootCallId = String(event.data.rootCallId);
496
496
  const parentCallId = String(event.data.parentCallId);
497
497
  const argumentsJson = serializeCodeDispatchArguments(event.data.arguments);
@@ -503,14 +503,14 @@ function exactToolApprovalCall(events, openTurn, callId, toolName) {
503
503
  seq: event.seq,
504
504
  name: event.data.name,
505
505
  arguments: argumentsJson,
506
- hashIdentity: ['tool/code-dispatch-start', event.seq, rootCallId, parentCallId,
506
+ hashIdentity: ['tool/ptc-dispatch-start', event.seq, rootCallId, parentCallId,
507
507
  callId, event.data.name, argumentsJson],
508
508
  });
509
509
  }
510
510
  if (event.type === 'tool/result'
511
511
  && String(event.data.message.source.callId) === callId)
512
512
  settled = true;
513
- if (event.type === 'tool/code-dispatch'
513
+ if (event.type === 'tool/ptc-dispatch'
514
514
  && String(event.data.subCallId) === callId)
515
515
  settled = true;
516
516
  }
@@ -560,6 +560,7 @@ export class AssistantDeliveryService extends Service {
560
560
  modelSelectionFlight;
561
561
  presentationFlight;
562
562
  workflowTraceFlight;
563
+ naturalObjectiveFlight;
563
564
  preferenceProjectionFlight;
564
565
  inboundAdmissionRecoveryFlight;
565
566
  inboundAdmissionRecoveryAfterSequence = 0;
@@ -696,6 +697,7 @@ export class AssistantDeliveryService extends Service {
696
697
  dispatchPreferenceFeedback: events => this.dispatchPreferenceFeedback(events),
697
698
  replyCompletedPreferenceTurn: (agent, binding, envelope, reply) => this.replyCompletedPreferenceTurn(agent, binding, envelope, reply),
698
699
  dispatchObjectiveFeedback: (binding, envelope, objectiveStatus) => this.dispatchObjectiveFeedback(binding, envelope, objectiveStatus),
700
+ dispatchNaturalObjectiveFeedback: (binding, envelope) => this.dispatchNaturalObjectiveFeedback(binding, envelope),
699
701
  dispatchWorkflowCommand: (binding, envelope, command) => this.dispatchWorkflowCommand(binding, envelope, command),
700
702
  dispatchLearningCommand: (binding, envelope, action, preferenceKey) => this.dispatchLearningCommand(binding, envelope, action, preferenceKey),
701
703
  authorizePermissionReply: (binding, envelope) => this.authorizePermissionReply(binding, envelope),
@@ -728,7 +730,8 @@ export class AssistantDeliveryService extends Service {
728
730
  // A plain approval/request listener can be starved by an earlier remote
729
731
  // waterfall (notably a Web-enabled Host with a headless Lark conversation).
730
732
  ctx.inject(['approval', 'assistantPolicy'], approvalCtx => approvalCtx.effect(() => {
731
- if (typeof approvalCtx.assistantPolicy.registerHumanApprovalAnswerer !== 'function') {
733
+ if (typeof approvalCtx.assistantPolicy.registerHumanApprovalAnswerer !== 'function'
734
+ || typeof approvalCtx.assistantPolicy.isAutoReviewEscalation !== 'function') {
732
735
  throw new Error('assistant-delivery: upgrade assistant-policy with Delivery; human approval routing is unavailable');
733
736
  }
734
737
  return approvalCtx.assistantPolicy.registerHumanApprovalAnswerer((request, next) => this.requestToolApproval(approvalCtx, request, next));
@@ -1843,6 +1846,83 @@ export class AssistantDeliveryService extends Service {
1843
1846
  runtime.ctx.effect(() => () => access.dispose(), 'assistant-delivery.native-web-runtime');
1844
1847
  return access;
1845
1848
  };
1849
+ /** Host-only proof of a single live native tool call in the exact claimed owner Inbox. */
1850
+ inspectOwnerForegroundToolCall(input) {
1851
+ if (!input || typeof input !== 'object'
1852
+ || typeof input.authorityId !== 'string' || input.authorityId.length === 0
1853
+ || typeof input.callId !== 'string' || input.callId.length === 0
1854
+ || typeof input.toolName !== 'string' || input.toolName.length === 0
1855
+ || typeof input.argumentsJson !== 'string'
1856
+ || Buffer.byteLength(input.authorityId) > toolApprovalIdentityBytes
1857
+ || Buffer.byteLength(input.callId) > toolApprovalIdentityBytes
1858
+ || Buffer.byteLength(input.toolName) > toolApprovalIdentityBytes
1859
+ || Buffer.byteLength(input.argumentsJson) > toolApprovalArgumentBytes)
1860
+ return undefined;
1861
+ const route = this.validateOwnerAgentForRoute(input.agent, input.authorityId);
1862
+ if (route === undefined)
1863
+ return undefined;
1864
+ const turn = this.currentPreferenceTurn(input.agent);
1865
+ if (turn === undefined || turn.principalId !== route.principalId
1866
+ || turn.principalLineage.principalRecordId !== route.principalRecordId
1867
+ || turn.principalLineage.principalVersion !== route.principalVersion
1868
+ || turn.scope.workspace !== route.workspace || turn.scope.preset !== route.agentPreset
1869
+ || turn.bindingVersion !== route.bindingVersion
1870
+ || turn.sessionId !== String(input.agent.session.id))
1871
+ return undefined;
1872
+ const task = this.deliveryStore.inspectPendingForegroundToolTask({
1873
+ inboxId: turn.sourceInboxId, sessionId: turn.sessionId, scope: turn.scope,
1874
+ owner: turn.principalLineage,
1875
+ binding: { id: turn.bindingId, version: turn.bindingVersion, generation: route.generation },
1876
+ });
1877
+ if (task === undefined)
1878
+ return undefined;
1879
+ const events = input.agent.session.snapshotEvents();
1880
+ const openTurn = events.findLast(event => event.type === 'turn/start' || event.type === 'turn/end');
1881
+ if (openTurn?.type !== 'turn/start' || openTurn.data.turn !== turn.turn)
1882
+ return undefined;
1883
+ const nativeCalls = events.filter((event) => event.type === 'tool/call' && event.seq > openTurn.seq
1884
+ && event.data.turn === turn.turn && String(event.data.callId) === input.callId);
1885
+ const unresolved = nativeCalls.filter(call => !events.some(event => event.type === 'tool/result'
1886
+ && event.seq > call.seq && String(event.data.message.source.callId) === String(call.data.callId)));
1887
+ if (unresolved.length !== 1)
1888
+ return undefined;
1889
+ const call = unresolved[0];
1890
+ const currentStep = events.findLast(event => (event.type === 'step/start' || event.type === 'step/end')
1891
+ && event.data.turn === turn.turn);
1892
+ if (String(call.data.callId) !== input.callId || call.data.name !== input.toolName
1893
+ || !Number.isSafeInteger(call.seq) || !Number.isSafeInteger(call.data.step)
1894
+ || typeof call.data.arguments !== 'string'
1895
+ || Buffer.byteLength(call.data.arguments) > toolApprovalArgumentBytes
1896
+ || events.some(event => event !== call && ((event.type === 'tool/call'
1897
+ && String(event.data.callId) === input.callId)
1898
+ || (event.type === 'tool/ptc-dispatch-start' && String(event.data.subCallId) === input.callId)))
1899
+ || currentStep?.type !== 'step/start' || currentStep.seq <= openTurn.seq
1900
+ || currentStep.seq >= call.seq || currentStep.data.step !== call.data.step
1901
+ || events.filter(event => event.type === 'step/start' && event.data.turn === turn.turn
1902
+ && event.data.step === call.data.step).length !== 1)
1903
+ return undefined;
1904
+ let normalizedArguments;
1905
+ try {
1906
+ const parsed = JSON.parse(call.data.arguments);
1907
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
1908
+ return undefined;
1909
+ normalizedArguments = JSON.stringify(parsed);
1910
+ }
1911
+ catch {
1912
+ return undefined;
1913
+ }
1914
+ const argumentsDigest = createHash('sha256').update(normalizedArguments).digest('hex');
1915
+ if (argumentsDigest !== createHash('sha256').update(input.argumentsJson).digest('hex'))
1916
+ return undefined;
1917
+ const eventDigest = createHash('sha256').update(JSON.stringify(['tool/call', call.seq,
1918
+ call.data.turn, call.data.step, input.callId, input.toolName, call.data.arguments])).digest('hex');
1919
+ if (this.deliveryStore.inspectPendingForegroundToolTask({ inboxId: task.inboxId,
1920
+ sessionId: task.sessionId, scope: task.scope, owner: task.owner, binding: task.binding }) === undefined)
1921
+ return undefined;
1922
+ return Object.freeze({ protocol: 'assistant-delivery/foreground-tool-call/v1', task,
1923
+ turn: turn.turn, call: Object.freeze({ id: input.callId, toolName: input.toolName,
1924
+ eventSeq: call.seq, eventDigest, argumentsDigest }) });
1925
+ }
1846
1926
  currentPreferenceTurn(agent) {
1847
1927
  this.assertActive();
1848
1928
  const attestation = this.preferenceTurns.get(agent);
@@ -1973,6 +2053,7 @@ export class AssistantDeliveryService extends Service {
1973
2053
  }
1974
2054
  const token = Symbol('assistant-delivery.trusted-evaluation');
1975
2055
  this.evaluationSink = Object.freeze({ token, registration });
2056
+ void this.drainNaturalObjectiveIntents();
1976
2057
  let active = true;
1977
2058
  return () => {
1978
2059
  if (!active)
@@ -2437,13 +2518,14 @@ export class AssistantDeliveryService extends Service {
2437
2518
  context: { initiator: 'external' },
2438
2519
  }).effect === 'allow';
2439
2520
  }
2440
- isOwnerFeedbackController(binding, envelope) {
2521
+ isOwnerFeedbackController(binding, envelope, allowProcessedNatural = false) {
2441
2522
  if (!this.isInboundAuthorized(binding, envelope))
2442
2523
  return false;
2443
2524
  const principal = this.deliveryStore.getPrincipal(binding.principal);
2444
2525
  const inbox = this.deliveryStore.getInboxByProviderEvent(envelope.channel, envelope.account, envelope.eventId);
2445
2526
  if (principal?.status !== 'active' || principal.role !== 'owner'
2446
- || inbox?.status !== 'claimed'
2527
+ || (inbox?.status !== 'claimed' && !(allowProcessedNatural && (inbox?.status === 'processed' || inbox?.status === 'dead_letter')
2528
+ && inbox.envelope.kind === 'text' && this.deliveryStore.getNaturalObjectiveIntent(inbox.id)?.envelopeHash === inbox.envelopeHash))
2447
2529
  || inbox.bindingId !== binding.id
2448
2530
  || JSON.stringify(inbox.envelope) !== JSON.stringify(envelope)) {
2449
2531
  return false;
@@ -2828,17 +2910,112 @@ export class AssistantDeliveryService extends Service {
2828
2910
  * Evolution re-proves the immutable production run; the later Inbox is a
2829
2911
  * separate evidence reference, never copied as prose.
2830
2912
  */
2913
+ async dispatchNaturalObjectiveFeedback(binding, envelope) {
2914
+ const selection = envelope.kind === 'text' && (envelope.attachments?.length ?? 0) === 0
2915
+ ? parseNaturalObjectiveFeedback(envelope.text) : undefined;
2916
+ if (selection === undefined || !this.isOwnerFeedbackController(binding, envelope))
2917
+ return;
2918
+ const replyTarget = envelope.metadata?.replyToProviderMessageId;
2919
+ if (typeof replyTarget !== 'string')
2920
+ return;
2921
+ const inbox = this.deliveryStore.getInboxByProviderEvent(envelope.channel, envelope.account, envelope.eventId);
2922
+ const target = this.deliveryStore.getOutboxByProviderMessage(envelope.channel, envelope.account, replyTarget);
2923
+ const sourceEventId = target?.intent.replyToEventId;
2924
+ const source = sourceEventId === undefined ? undefined
2925
+ : this.deliveryStore.getInboxByProviderEvent(envelope.channel, envelope.account, sourceEventId);
2926
+ const lineage = this.ownerLineageForBinding(binding);
2927
+ if (inbox === undefined || target === undefined || source === undefined || lineage === undefined)
2928
+ return;
2929
+ if (!this.deliveryStore.isVerifiedForegroundObjectiveTarget(binding, source.id, target.id))
2930
+ return;
2931
+ const scope = { workspace: binding.workspace, preset: binding.agentPreset };
2932
+ const execution = this.deliveryStore.inspectForegroundExecutionForOwner({ inboxId: source.id,
2933
+ scope, owner: lineage, bindingId: binding.id, bindingVersion: binding.version,
2934
+ bindingGeneration: binding.generation })
2935
+ ?? this.deliveryStore.inspectForegroundAcceptedExecutionForOwner({ inboxId: source.id,
2936
+ scope, owner: lineage, bindingId: binding.id });
2937
+ if (execution?.status !== 'succeeded' || !execution.quiescent)
2938
+ return;
2939
+ try {
2940
+ this.deliveryStore.prepareNaturalObjectiveIntent({ binding, feedbackInboxId: inbox.id,
2941
+ sourceInboxId: source.id, sourceOutboxId: target.id, selection });
2942
+ }
2943
+ catch (error) {
2944
+ if (error instanceof DeliveryStoreError)
2945
+ return;
2946
+ throw error;
2947
+ }
2948
+ void this.drainNaturalObjectiveIntents().catch(() => { });
2949
+ }
2950
+ drainNaturalObjectiveIntents() {
2951
+ if (this.naturalObjectiveFlight !== undefined)
2952
+ return this.naturalObjectiveFlight;
2953
+ const flight = this.runNaturalObjectiveIntents()
2954
+ .finally(() => { if (this.naturalObjectiveFlight === flight)
2955
+ this.naturalObjectiveFlight = undefined; });
2956
+ this.naturalObjectiveFlight = flight;
2957
+ return flight;
2958
+ }
2959
+ async runNaturalObjectiveIntents() {
2960
+ let afterAdmissionSequence = 0;
2961
+ while (true) {
2962
+ const page = this.deliveryStore.listPendingNaturalObjectiveIntents(100, afterAdmissionSequence);
2963
+ if (page.length === 0)
2964
+ return;
2965
+ for (const intent of page) {
2966
+ afterAdmissionSequence = intent.admissionSequence;
2967
+ if (!this.active || this.evaluationSink === undefined)
2968
+ return;
2969
+ if (this.evaluationSink.registration.ownerRevisionProtocol !== 'owner-objective-revision/v2'
2970
+ || typeof this.evaluationSink.registration.inspect !== 'function')
2971
+ return;
2972
+ const inbox = this.deliveryStore.getInbox(intent.inboxId);
2973
+ const binding = this.deliveryStore.getBinding(intent.bindingId);
2974
+ if (inbox === undefined || binding === undefined)
2975
+ continue;
2976
+ let frozen = intent;
2977
+ if (frozen.status === 'awaiting-evaluation') {
2978
+ const inspected = await this.dispatchObjectiveFeedback(binding, inbox.envelope, { kind: 'objective-status' });
2979
+ if (inspected === 'invalid-target' || inspected === 'conflict') {
2980
+ this.deliveryStore.settleNaturalObjectiveIntent(intent.inboxId, 'conflict');
2981
+ continue;
2982
+ }
2983
+ if (inspected === 'unavailable' || inspected === 'unknown')
2984
+ continue;
2985
+ const current = typeof inspected === 'object' ? inspected : undefined;
2986
+ frozen = this.deliveryStore.freezeNaturalObjectiveIntent(intent.inboxId, current) ?? intent;
2987
+ if (frozen.status === 'awaiting-evaluation')
2988
+ continue;
2989
+ }
2990
+ if (frozen.status !== 'frozen' || frozen.command === undefined)
2991
+ continue;
2992
+ const result = await this.dispatchObjectiveFeedback(binding, inbox.envelope, frozen.command);
2993
+ if (typeof result === 'object') {
2994
+ this.deliveryStore.settleNaturalObjectiveIntent(intent.inboxId, 'projected', result);
2995
+ }
2996
+ else if (result === 'conflict' || result === 'invalid-target') {
2997
+ this.deliveryStore.settleNaturalObjectiveIntent(intent.inboxId, 'conflict');
2998
+ }
2999
+ else if (result === 'unavailable' || result === 'unknown')
3000
+ continue;
3001
+ }
3002
+ if (page.length < 100)
3003
+ return;
3004
+ }
3005
+ }
2831
3006
  async dispatchObjectiveFeedback(binding, envelope, command) {
2832
3007
  const parsed = typeof command === 'string' ? { kind: 'objective', objectiveStatus: command } : command;
2833
3008
  const objectiveStatus = parsed.kind === 'objective-status' ? 'achieved' : parsed.objectiveStatus;
2834
3009
  const replyTarget = envelope.metadata?.replyToProviderMessageId;
2835
3010
  if (typeof replyTarget !== 'string')
2836
3011
  return 'invalid-target';
2837
- if (!this.isOwnerFeedbackController(binding, envelope))
2838
- return 'invalid-target';
2839
3012
  const inbox = this.deliveryStore.getInboxByProviderEvent(envelope.channel, envelope.account, envelope.eventId);
3013
+ const naturalIntent = inbox === undefined ? undefined : this.deliveryStore.getNaturalObjectiveIntent(inbox.id);
3014
+ if (!this.isOwnerFeedbackController(binding, envelope, naturalIntent !== undefined))
3015
+ return 'invalid-target';
2840
3016
  const target = this.deliveryStore.getOutboxByProviderMessage(envelope.channel, envelope.account, replyTarget);
2841
- if (inbox?.status !== 'claimed' || inbox.bindingId !== binding.id
3017
+ if ((inbox?.status !== 'claimed' && !(naturalIntent !== undefined
3018
+ && (inbox?.status === 'processed' || inbox?.status === 'dead_letter'))) || inbox.bindingId !== binding.id
2842
3019
  || target === undefined || target.providerMessageId !== replyTarget
2843
3020
  || target.intent.bindingId !== binding.id
2844
3021
  || JSON.stringify(target.intent.target.conversation) !== JSON.stringify(binding.conversation)
@@ -2846,9 +3023,22 @@ export class AssistantDeliveryService extends Service {
2846
3023
  return 'invalid-target';
2847
3024
  }
2848
3025
  const hostCommand = parseDeliveryCommand(inbox.envelope);
2849
- if (hostCommand?.name !== 'feedback' || (inbox.envelope.attachments?.length ?? 0) !== 0
2850
- || JSON.stringify(parseFeedbackCommand(hostCommand.rawInput)) !== JSON.stringify(parsed))
3026
+ const typedMatches = hostCommand?.name === 'feedback'
3027
+ && JSON.stringify(parseFeedbackCommand(hostCommand.rawInput)) === JSON.stringify(parsed);
3028
+ const naturalMatches = naturalIntent !== undefined && inbox.envelope.kind === 'text'
3029
+ && naturalIntent.envelopeHash === inbox.envelopeHash
3030
+ && naturalIntent.sourceOutboxId === target.id
3031
+ && parseNaturalObjectiveFeedback(inbox.envelope.text) === naturalIntent.selection
3032
+ && (parsed.kind === 'objective-status' || (naturalIntent.status === 'frozen'
3033
+ && JSON.stringify(naturalIntent.command) === JSON.stringify(parsed)));
3034
+ if ((inbox.envelope.attachments?.length ?? 0) !== 0 || (!typedMatches && !naturalMatches))
2851
3035
  return 'invalid-target';
3036
+ if (naturalIntent === undefined && parsed.kind !== 'objective-status'
3037
+ && this.deliveryStore.hasEarlierPendingNaturalObjectiveIntent(target.id, inbox.admissionCursor.sequence)) {
3038
+ await this.drainNaturalObjectiveIntents();
3039
+ if (this.deliveryStore.hasEarlierPendingNaturalObjectiveIntent(target.id, inbox.admissionCursor.sequence))
3040
+ return 'unknown';
3041
+ }
2852
3042
  const metadata = target.intent.metadata;
2853
3043
  let goalOutcomeTarget;
2854
3044
  try {
@@ -2874,6 +3064,8 @@ export class AssistantDeliveryService extends Service {
2874
3064
  const sourceInbox = this.deliveryStore.getInboxByProviderEvent(envelope.channel, envelope.account, sourceEventId);
2875
3065
  if (sourceInbox === undefined)
2876
3066
  return 'invalid-target';
3067
+ if (!this.deliveryStore.isVerifiedForegroundObjectiveTarget(binding, sourceInbox.id, target.id))
3068
+ return 'invalid-target';
2877
3069
  const lineage = this.ownerLineageForBinding(binding);
2878
3070
  const accepted = lineage === undefined ? null : this.deliveryStore.inspectForegroundAcceptedExecutionForOwner({
2879
3071
  inboxId: sourceInbox.id,
@@ -2892,7 +3084,7 @@ export class AssistantDeliveryService extends Service {
2892
3084
  && (execution.status !== 'succeeded' || !execution.quiescent)))
2893
3085
  return 'invalid-target';
2894
3086
  const canonicalForeground = foreground !== null || accepted !== null;
2895
- const projectForegroundOwner = async (inspectOnly) => {
3087
+ const projectForegroundOwner = (inspectOnly) => {
2896
3088
  const sink = this.evaluationSink;
2897
3089
  if (sink === undefined || lineage === undefined)
2898
3090
  return undefined;
@@ -2921,10 +3113,10 @@ export class AssistantDeliveryService extends Service {
2921
3113
  return undefined;
2922
3114
  if (inspectOnly)
2923
3115
  return sink.registration.inspect?.(capabilityReceipt);
2924
- const receipt = await Promise.resolve(sink.registration.append({
3116
+ const receipt = sink.registration.append({
2925
3117
  capabilityReceipt, runId: sourceInbox.id, outboxId: target.id, chatId: binding.conversation.chat,
2926
3118
  principalId: externalPrincipalId(binding.principal), bindingId: binding.id, idempotencyKey: claims.idempotencyKey,
2927
- }));
3119
+ });
2928
3120
  return typeof receipt === 'object' && receipt !== null
2929
3121
  && typeof receipt.ownerFeedbackState === 'object'
2930
3122
  ? receipt.ownerFeedbackState
@@ -2934,12 +3126,15 @@ export class AssistantDeliveryService extends Service {
2934
3126
  if (!canonicalForeground)
2935
3127
  return this.deliveryStore.verifiedWorkflowObjectiveState(target.id, lineage) ?? 'recorded';
2936
3128
  try {
2937
- return await projectForegroundOwner(true) ?? 'unavailable';
3129
+ const inspected = projectForegroundOwner(true);
3130
+ return inspected ?? (naturalIntent !== undefined && this.evaluationSink !== undefined ? 'recorded' : 'unavailable');
2938
3131
  }
2939
3132
  catch {
2940
3133
  return 'unknown';
2941
3134
  }
2942
3135
  }
3136
+ if (naturalIntent !== undefined && !canonicalForeground)
3137
+ return 'invalid-target';
2943
3138
  if (canonicalForeground && this.evaluationSink === undefined)
2944
3139
  return 'unavailable';
2945
3140
  try {
@@ -2947,7 +3142,7 @@ export class AssistantDeliveryService extends Service {
2947
3142
  // is unavailable, never acknowledge a Delivery-only success; a replay
2948
3143
  // can safely repeat the opaque command and then repair local workflow
2949
3144
  // evidence after the canonical owner revision is durable.
2950
- const canonical = canonicalForeground ? await projectForegroundOwner(false) : undefined;
3145
+ const canonical = canonicalForeground ? projectForegroundOwner(false) : undefined;
2951
3146
  if (canonicalForeground && canonical === undefined)
2952
3147
  return 'unavailable';
2953
3148
  const result = this.deliveryStore.commitVerifiedWorkflowTraceFeedback({
@@ -2958,7 +3153,7 @@ export class AssistantDeliveryService extends Service {
2958
3153
  objectiveStatus,
2959
3154
  ...(parsed.kind === 'objective-revision' ? { command: parsed } : {}),
2960
3155
  });
2961
- await this.drainWorkflowTraces();
3156
+ void this.drainWorkflowTraces().catch(() => { });
2962
3157
  return canonical ?? result.ownerFeedbackState ?? 'recorded';
2963
3158
  }
2964
3159
  catch (error) {
@@ -2974,7 +3169,7 @@ export class AssistantDeliveryService extends Service {
2974
3169
  if (typeof error === 'object' && error !== null && 'code' in error
2975
3170
  && ['idempotency-conflict', 'version-conflict'].includes(String(error.code)))
2976
3171
  return 'conflict';
2977
- return 'invalid-target';
3172
+ return 'unknown';
2978
3173
  }
2979
3174
  }
2980
3175
  const automationId = metadata?.['dsh.learning.automationId'];
@@ -3264,7 +3459,7 @@ export class AssistantDeliveryService extends Service {
3264
3459
  const reviewer = approvalReviewerOf(events);
3265
3460
  const reviewRoute = reviewer === 'user'
3266
3461
  ? 'user'
3267
- : reviewer === 'auto-review' && isAutoReviewEscalation(request)
3462
+ : reviewer === 'auto-review' && ctx.assistantPolicy.isAutoReviewEscalation(request)
3268
3463
  ? 'auto-escalation'
3269
3464
  : undefined;
3270
3465
  const sessionId = String(agent.session.id);
@@ -3407,7 +3602,7 @@ export class AssistantDeliveryService extends Service {
3407
3602
  }
3408
3603
  const reviewRoute = reviewer === 'user'
3409
3604
  ? 'user'
3410
- : reviewer === 'auto-review' && isAutoReviewEscalation(request)
3605
+ : reviewer === 'auto-review' && ctx.assistantPolicy.isAutoReviewEscalation(request)
3411
3606
  ? 'auto-escalation'
3412
3607
  : undefined;
3413
3608
  if (reviewRoute === undefined)
@@ -3974,6 +4169,94 @@ export class AssistantDeliveryService extends Service {
3974
4169
  generation: resolved.snapshot.generation,
3975
4170
  });
3976
4171
  }
4172
+ /** Host-only proof that the live caller Agent belongs to the current owner route. */
4173
+ validateOwnerAgentForRoute(agent, authorityId) {
4174
+ if (!this.active || typeof agent !== 'object' || agent === null || agent.session === undefined)
4175
+ return undefined;
4176
+ const agents = this.context.get('agents');
4177
+ const sessions = this.context.get('sessions');
4178
+ if (agents === undefined || sessions === undefined)
4179
+ return undefined;
4180
+ const sessionId = String(agent.session.id);
4181
+ const liveCaller = () => String(agent.id) === sessionId
4182
+ && agents.get(agent.id) === agent
4183
+ && sessions.get(agent.id) === agent.session;
4184
+ if (!liveCaller())
4185
+ return undefined;
4186
+ const matches = (attestation, route, receipt) => attestation.sessionId === sessionId
4187
+ && attestation.bindingId === route.binding.id
4188
+ && attestation.bindingVersion === route.binding.version
4189
+ && attestation.bindingGeneration === route.binding.generation
4190
+ && route.binding.sessionId === sessionId
4191
+ && route.snapshot.bindingId === attestation.bindingId
4192
+ && route.snapshot.bindingVersion === attestation.bindingVersion
4193
+ && route.snapshot.generation === attestation.bindingGeneration
4194
+ && attestation.principalId === receipt.principalId
4195
+ && attestation.principalLineage.principalRecordId === receipt.principalRecordId
4196
+ && attestation.principalLineage.principalVersion === receipt.principalVersion
4197
+ && attestation.scope.workspace === receipt.workspace
4198
+ && attestation.scope.preset === receipt.agentPreset
4199
+ && agent.session.header.cwd === receipt.workspace
4200
+ && agent.session.header.agentPreset === receipt.agentPreset
4201
+ && route.authorityId === receipt.authorityId
4202
+ && route.snapshot.authorityHash === receipt.authorityHash
4203
+ && route.snapshot.bindingVersion === receipt.bindingVersion
4204
+ && route.snapshot.generation === receipt.generation;
4205
+ try {
4206
+ const first = this.preferencePrincipalForAgent(agent);
4207
+ if (first === undefined)
4208
+ return undefined;
4209
+ const route = this.resolveOwnerRoute(authorityId);
4210
+ const receipt = this.validateOwnerRoute({
4211
+ authorityId: route.authorityId,
4212
+ principalId: first.principalId,
4213
+ workspace: first.scope.workspace,
4214
+ agentPreset: first.scope.preset,
4215
+ });
4216
+ if (!matches(first, route, receipt))
4217
+ return undefined;
4218
+ const current = this.preferencePrincipalForAgent(agent);
4219
+ const currentRoute = this.resolveOwnerRoute(authorityId);
4220
+ return current !== undefined && liveCaller() && matches(current, currentRoute, receipt)
4221
+ ? receipt
4222
+ : undefined;
4223
+ }
4224
+ catch (error) {
4225
+ if (error instanceof AssistantDeliveryError && error.code === 'missing-binding')
4226
+ return undefined;
4227
+ throw error;
4228
+ }
4229
+ }
4230
+ /** Host-only durable ordinary-turn metadata; never registered as a model tool. */
4231
+ listOwnerForegroundTaskSources(input) {
4232
+ this.assertActive();
4233
+ this.validateOwnerRoute(input);
4234
+ return this.deliveryStore.listOwnerForegroundTaskSources(input, this.ownerRoutes.get(input.authorityId));
4235
+ }
4236
+ /** Host-only exact terminal source metadata, including before reply Outbox exists. */
4237
+ inspectOwnerForegroundTaskSource(input) {
4238
+ this.assertActive();
4239
+ this.validateOwnerRoute(input);
4240
+ return this.deliveryStore.inspectOwnerForegroundTaskSource(input, this.ownerRoutes.get(input.authorityId));
4241
+ }
4242
+ /** Read one exact completed source under the current owner fence. */
4243
+ readOwnerForegroundTaskSource(input) {
4244
+ this.assertActive();
4245
+ this.validateOwnerRoute(input);
4246
+ return this.deliveryStore.readOwnerForegroundTaskSource(input, this.ownerRoutes.get(input.authorityId));
4247
+ }
4248
+ /** Hold Delivery's owner/source writer fence across synchronous downstream adoption. */
4249
+ withOwnerForegroundTaskSourceFence(input, callback) {
4250
+ this.assertActive();
4251
+ this.validateOwnerRoute(input);
4252
+ return this.deliveryStore.withOwnerForegroundTaskSourceFence(input, this.ownerRoutes.get(input.authorityId), callback);
4253
+ }
4254
+ /** Hold one owner/source writer fence for a bounded, ordered batch of sources. */
4255
+ withOwnerForegroundTaskSourcesFence(input, callback) {
4256
+ this.assertActive();
4257
+ this.validateOwnerRoute(input);
4258
+ return this.deliveryStore.withOwnerForegroundTaskSourcesFence(input, this.ownerRoutes.get(input.authorityId), callback);
4259
+ }
3977
4260
  /** Host-only snapshot of the model selected for this owner's external conversation. */
3978
4261
  inspectOwnerModelSelection(input) {
3979
4262
  this.validateOwnerRoute(input);
@@ -4037,6 +4320,19 @@ export class AssistantDeliveryService extends Service {
4037
4320
  owner: { principalRecordId: owner.principalRecordId, principalVersion: owner.principalVersion } }) ?? accepted;
4038
4321
  if (execution === null)
4039
4322
  return undefined;
4323
+ // /new retires the conversation binding. Read the version from the actual
4324
+ // execution row, never the retired binding's incremented version or the
4325
+ // new Session's generation. The Store also fences the current owner route.
4326
+ const original = this.deliveryStore.inspectOwnerForegroundTaskSource({ ...routeInput,
4327
+ inboxId: inbox.id, expectedOwner: { authorityHash: owner.authorityHash,
4328
+ principalRecordId: owner.principalRecordId, principalVersion: owner.principalVersion } }, this.ownerRoutes.get(owner.authorityId));
4329
+ if (original === undefined || original.binding.id !== binding.id
4330
+ || original.binding.sessionId !== binding.sessionId)
4331
+ return undefined;
4332
+ const sourceOwner = Object.freeze({ ...owner, bindingVersion: original.binding.version,
4333
+ generation: original.binding.generation });
4334
+ if (!isSourceOwnerContinuation(owner, sourceOwner))
4335
+ return undefined;
4040
4336
  const verifierComponent = (component) => accepted !== null && component.source.kind === 'evaluator' && component.source.id === 'assistant-verifier'
4041
4337
  && component.evaluator.id === 'assistant-verifier' && component.evaluator.version === '1'
4042
4338
  && component.evidence.some(ref => ref.kind === 'foreground-turn' && ref.ref === inbox.id)
@@ -4048,6 +4344,8 @@ export class AssistantDeliveryService extends Service {
4048
4344
  return undefined;
4049
4345
  let judgement = 'unresolved';
4050
4346
  let ownerRevision;
4347
+ let feedback;
4348
+ let feedbackOutboxId;
4051
4349
  const objective = canonical.objective;
4052
4350
  if (objective?.source.kind === 'user-feedback') {
4053
4351
  if (objective.source.id !== 'assistant-delivery/typed-owner-feedback')
@@ -4068,7 +4366,10 @@ export class AssistantDeliveryService extends Service {
4068
4366
  || !['accepted', 'delivered', 'read'].includes(outbox.status))
4069
4367
  return undefined;
4070
4368
  judgement = 'owner-feedback';
4369
+ feedbackOutboxId = outbox.id;
4071
4370
  ownerRevision = Object.freeze({ version: revision.version, action: revision.action });
4371
+ if (revision.operationId !== undefined)
4372
+ feedback = this.deliveryStore.findNaturalObjectiveFeedbackByOperation(outbox.id, revision.operationId, { principalRecordId: owner.principalRecordId, principalVersion: owner.principalVersion });
4072
4373
  }
4073
4374
  else if (objective !== undefined) {
4074
4375
  if (!verifierComponent(objective))
@@ -4080,16 +4381,36 @@ export class AssistantDeliveryService extends Service {
4080
4381
  // may turn a completed ordinary conversation into a learning source.
4081
4382
  if (accepted === null && judgement !== 'owner-feedback')
4082
4383
  return undefined;
4384
+ const content = feedbackOutboxId === undefined ? undefined : this.readOwnerForegroundTaskSource({
4385
+ ...routeInput, inboxId: inbox.id, expectedSourceDigest: original.sourceDigest,
4386
+ expectedOwner: { authorityHash: owner.authorityHash,
4387
+ principalRecordId: owner.principalRecordId, principalVersion: owner.principalVersion },
4388
+ maxReplyBytes: 16_384,
4389
+ });
4390
+ if (content !== undefined && content.reply.outboxId !== feedbackOutboxId)
4391
+ return undefined;
4392
+ const reply = content?.reply === undefined ? undefined : (() => {
4393
+ // A partial prefix could cut a credential below the filter's minimum
4394
+ // token length. Publish no raw prefix when the reader hit its hard cap.
4395
+ const visible = content.reply.truncated
4396
+ ? { text: '[TRUNCATED REPLY]', truncated: true, redacted: true }
4397
+ : redactOwnerLearningReply(content.reply.text, 4096);
4398
+ return Object.freeze({ text: visible.text, truncated: content.reply.truncated || visible.truncated,
4399
+ redacted: visible.redacted, outboxId: content.reply.outboxId,
4400
+ intentDigest: content.reply.intentDigest, fullTextDigest: content.reply.fullTextDigest });
4401
+ })();
4083
4402
  // Fence cross-ledger reads before handing any task text to the consumer.
4084
4403
  if (!evaluation.isTrustedTaskLearningProjectionReceipt(canonical)
4085
4404
  || acceptanceCanonicalJson(owner) !== acceptanceCanonicalJson(this.validateOwnerRoute(routeInput)))
4086
4405
  return undefined;
4087
4406
  const objectiveText = inbox.envelope.text;
4088
- return Object.freeze({ protocol: 'assistant-delivery/owner-foreground-learning/v1', owner, canonical, judgement,
4407
+ return Object.freeze({ protocol: 'assistant-delivery/owner-foreground-learning/v1', owner: sourceOwner, canonical, judgement,
4089
4408
  ...(ownerRevision === undefined ? {} : { ownerRevision }),
4409
+ ...(feedback === undefined ? {} : { feedback }),
4090
4410
  source: Object.freeze({ sessionId: binding.sessionId, inboxId: inbox.id,
4091
4411
  objective: objectiveText.slice(0, 4096), truncated: objectiveText.length > 4096, quiescent: execution.quiescent,
4092
- modelSelectionState: execution.modelSelectionState, ...(execution.modelSelection === undefined ? {} : { modelSelection: execution.modelSelection }) }) });
4412
+ modelSelectionState: execution.modelSelectionState, ...(execution.modelSelection === undefined ? {} : { modelSelection: execution.modelSelection }),
4413
+ ...(reply === undefined ? {} : { reply }) }) });
4093
4414
  }
4094
4415
  /**
4095
4416
  * Enqueue against a stable Host-owned route rather than a session binding.
@@ -4366,6 +4687,7 @@ export class AssistantDeliveryService extends Service {
4366
4687
  await this.outbound.tick();
4367
4688
  await this.drainDeliveryPresentations();
4368
4689
  await this.drainPreferenceProjections();
4690
+ await this.drainNaturalObjectiveIntents();
4369
4691
  await this.drainWorkflowTraces();
4370
4692
  }
4371
4693
  async whenIdle() {
@@ -4376,6 +4698,7 @@ export class AssistantDeliveryService extends Service {
4376
4698
  this.modelSelectionFlight,
4377
4699
  this.presentationFlight,
4378
4700
  this.preferenceProjectionFlight,
4701
+ this.naturalObjectiveFlight,
4379
4702
  this.workflowTraceFlight,
4380
4703
  this.inboundAdmissionRecoveryFlight,
4381
4704
  ]);
@@ -4628,6 +4951,7 @@ export class AssistantDeliveryService extends Service {
4628
4951
  this.modelSelectionFlight,
4629
4952
  this.presentationFlight,
4630
4953
  this.preferenceProjectionFlight,
4954
+ this.naturalObjectiveFlight,
4631
4955
  this.workflowTraceFlight,
4632
4956
  ]);
4633
4957
  }