@namzu/sdk 54.0.0 → 55.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/agents/QueryAgent.d.ts.map +1 -1
  3. package/dist/agents/QueryAgent.js +1 -0
  4. package/dist/agents/QueryAgent.js.map +1 -1
  5. package/dist/compaction/extractor.d.ts.map +1 -1
  6. package/dist/compaction/extractor.js +5 -1
  7. package/dist/compaction/extractor.js.map +1 -1
  8. package/dist/compaction/managers/structured.d.ts.map +1 -1
  9. package/dist/compaction/managers/structured.js +5 -0
  10. package/dist/compaction/managers/structured.js.map +1 -1
  11. package/dist/compaction/manual.d.ts.map +1 -1
  12. package/dist/compaction/manual.js +6 -0
  13. package/dist/compaction/manual.js.map +1 -1
  14. package/dist/compaction/serializer.d.ts +6 -0
  15. package/dist/compaction/serializer.d.ts.map +1 -1
  16. package/dist/compaction/serializer.js +21 -3
  17. package/dist/compaction/serializer.js.map +1 -1
  18. package/dist/compaction/verifier.d.ts.map +1 -1
  19. package/dist/compaction/verifier.js +27 -6
  20. package/dist/compaction/verifier.js.map +1 -1
  21. package/dist/constants/compaction/index.d.ts +1 -0
  22. package/dist/constants/compaction/index.d.ts.map +1 -1
  23. package/dist/constants/compaction/index.js +1 -0
  24. package/dist/constants/compaction/index.js.map +1 -1
  25. package/dist/manager/resident/proposal.d.ts +2 -2
  26. package/dist/manager/resident/store.d.ts +4 -4
  27. package/dist/prompt/coding-agent-doctrine.d.ts +3 -3
  28. package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
  29. package/dist/prompt/coding-agent-doctrine.js +18 -20
  30. package/dist/prompt/coding-agent-doctrine.js.map +1 -1
  31. package/dist/public-runtime.d.ts +1 -1
  32. package/dist/public-runtime.d.ts.map +1 -1
  33. package/dist/public-runtime.js +1 -1
  34. package/dist/public-runtime.js.map +1 -1
  35. package/dist/runtime/query/checkpoint.d.ts +12 -2
  36. package/dist/runtime/query/checkpoint.d.ts.map +1 -1
  37. package/dist/runtime/query/checkpoint.js +42 -8
  38. package/dist/runtime/query/checkpoint.js.map +1 -1
  39. package/dist/runtime/query/executor.d.ts +39 -1
  40. package/dist/runtime/query/executor.d.ts.map +1 -1
  41. package/dist/runtime/query/executor.js +115 -7
  42. package/dist/runtime/query/executor.js.map +1 -1
  43. package/dist/runtime/query/index.d.ts +18 -0
  44. package/dist/runtime/query/index.d.ts.map +1 -1
  45. package/dist/runtime/query/index.js +20 -1
  46. package/dist/runtime/query/index.js.map +1 -1
  47. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  48. package/dist/runtime/query/iteration/index.js +17 -8
  49. package/dist/runtime/query/iteration/index.js.map +1 -1
  50. package/dist/runtime/query/iteration/phases/context.d.ts +24 -2
  51. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  52. package/dist/runtime/query/iteration/phases/context.js +22 -22
  53. package/dist/runtime/query/iteration/phases/context.js.map +1 -1
  54. package/dist/runtime/query/iteration/phases/per-call-review.d.ts +78 -0
  55. package/dist/runtime/query/iteration/phases/per-call-review.d.ts.map +1 -0
  56. package/dist/runtime/query/iteration/phases/per-call-review.js +494 -0
  57. package/dist/runtime/query/iteration/phases/per-call-review.js.map +1 -0
  58. package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
  59. package/dist/runtime/query/iteration/phases/tool-review.js +129 -62
  60. package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
  61. package/dist/runtime/query/iteration/step-context.d.ts +14 -0
  62. package/dist/runtime/query/iteration/step-context.d.ts.map +1 -1
  63. package/dist/runtime/query/iteration/step-context.js +29 -0
  64. package/dist/runtime/query/iteration/step-context.js.map +1 -1
  65. package/dist/runtime/query/iteration/step-shaping.d.ts.map +1 -1
  66. package/dist/runtime/query/iteration/step-shaping.js +6 -2
  67. package/dist/runtime/query/iteration/step-shaping.js.map +1 -1
  68. package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
  69. package/dist/runtime/query/prepare-turn.js +16 -3
  70. package/dist/runtime/query/prepare-turn.js.map +1 -1
  71. package/dist/runtime/query/project-instructions.d.ts +24 -5
  72. package/dist/runtime/query/project-instructions.d.ts.map +1 -1
  73. package/dist/runtime/query/project-instructions.js +107 -35
  74. package/dist/runtime/query/project-instructions.js.map +1 -1
  75. package/dist/runtime/query/prompt.d.ts +6 -0
  76. package/dist/runtime/query/prompt.d.ts.map +1 -1
  77. package/dist/runtime/query/prompt.js +10 -49
  78. package/dist/runtime/query/prompt.js.map +1 -1
  79. package/dist/runtime/query/resume-pending.d.ts +8 -0
  80. package/dist/runtime/query/resume-pending.d.ts.map +1 -1
  81. package/dist/runtime/query/resume-pending.js +101 -0
  82. package/dist/runtime/query/resume-pending.js.map +1 -1
  83. package/dist/runtime/query/review-policy.d.ts +18 -0
  84. package/dist/runtime/query/review-policy.d.ts.map +1 -1
  85. package/dist/runtime/query/review-policy.js +10 -0
  86. package/dist/runtime/query/review-policy.js.map +1 -1
  87. package/dist/runtime/query/steering.d.ts +55 -56
  88. package/dist/runtime/query/steering.d.ts.map +1 -1
  89. package/dist/runtime/query/steering.js +78 -72
  90. package/dist/runtime/query/steering.js.map +1 -1
  91. package/dist/tools/builtins/bash.d.ts +8 -0
  92. package/dist/tools/builtins/bash.d.ts.map +1 -1
  93. package/dist/tools/builtins/bash.js +13 -18
  94. package/dist/tools/builtins/bash.js.map +1 -1
  95. package/dist/tools/builtins/browser.d.ts.map +1 -1
  96. package/dist/tools/builtins/browser.js +38 -12
  97. package/dist/tools/builtins/browser.js.map +1 -1
  98. package/dist/tools/builtins/edit.js +4 -4
  99. package/dist/tools/builtins/edit.js.map +1 -1
  100. package/dist/tools/builtins/job.js +1 -1
  101. package/dist/tools/builtins/job.js.map +1 -1
  102. package/dist/tools/builtins/skill.js +1 -1
  103. package/dist/tools/builtins/skill.js.map +1 -1
  104. package/dist/tools/builtins/structuredOutput.js +1 -1
  105. package/dist/tools/builtins/structuredOutput.js.map +1 -1
  106. package/dist/tools/builtins/verify-outputs.js +1 -1
  107. package/dist/tools/builtins/verify-outputs.js.map +1 -1
  108. package/dist/tools/builtins/write-file.js +4 -4
  109. package/dist/tools/builtins/write-file.js.map +1 -1
  110. package/dist/tools/coordinator/ask-user-question.d.ts +6 -0
  111. package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
  112. package/dist/tools/coordinator/ask-user-question.js +6 -1
  113. package/dist/tools/coordinator/ask-user-question.js.map +1 -1
  114. package/dist/tools/coordinator/index.d.ts.map +1 -1
  115. package/dist/tools/coordinator/index.js +10 -3
  116. package/dist/tools/coordinator/index.js.map +1 -1
  117. package/dist/tools/schedules/loop-tool.d.ts.map +1 -1
  118. package/dist/tools/schedules/loop-tool.js +4 -2
  119. package/dist/tools/schedules/loop-tool.js.map +1 -1
  120. package/dist/tools/schedules/schedule-tool.js +2 -2
  121. package/dist/tools/schedules/schedule-tool.js.map +1 -1
  122. package/dist/tools/task/create.js +1 -1
  123. package/dist/tools/task/create.js.map +1 -1
  124. package/dist/tools/untrusted-envelope.js +1 -1
  125. package/dist/tools/untrusted-envelope.js.map +1 -1
  126. package/dist/turn/evidence-query.d.ts.map +1 -1
  127. package/dist/turn/evidence-query.js +16 -1
  128. package/dist/turn/evidence-query.js.map +1 -1
  129. package/dist/turn/evidence-recall.js +1 -1
  130. package/dist/turn/evidence-recall.js.map +1 -1
  131. package/dist/types/agent/base.d.ts +7 -0
  132. package/dist/types/agent/base.d.ts.map +1 -1
  133. package/dist/types/hitl/index.d.ts +66 -0
  134. package/dist/types/hitl/index.d.ts.map +1 -1
  135. package/dist/types/hitl/index.js.map +1 -1
  136. package/dist/types/message/index.d.ts +8 -0
  137. package/dist/types/message/index.d.ts.map +1 -1
  138. package/dist/types/message/index.js +12 -2
  139. package/dist/types/message/index.js.map +1 -1
  140. package/dist/types/message/notice-frame.d.ts +41 -0
  141. package/dist/types/message/notice-frame.d.ts.map +1 -0
  142. package/dist/types/message/notice-frame.js +65 -0
  143. package/dist/types/message/notice-frame.js.map +1 -0
  144. package/package.json +1 -1
  145. package/src/agents/QueryAgent.ts +1 -0
  146. package/src/compaction/extractor.ts +8 -1
  147. package/src/compaction/managers/structured.ts +3 -0
  148. package/src/compaction/manual.ts +5 -0
  149. package/src/compaction/serializer.ts +28 -3
  150. package/src/compaction/verifier.ts +32 -6
  151. package/src/constants/compaction/index.ts +1 -0
  152. package/src/prompt/coding-agent-doctrine.ts +18 -20
  153. package/src/public-runtime.ts +1 -0
  154. package/src/runtime/query/checkpoint.ts +49 -8
  155. package/src/runtime/query/executor.ts +165 -15
  156. package/src/runtime/query/index.ts +45 -1
  157. package/src/runtime/query/iteration/index.ts +19 -6
  158. package/src/runtime/query/iteration/phases/context.ts +37 -11
  159. package/src/runtime/query/iteration/phases/per-call-review.ts +598 -0
  160. package/src/runtime/query/iteration/phases/tool-review.ts +168 -83
  161. package/src/runtime/query/iteration/step-context.ts +29 -0
  162. package/src/runtime/query/iteration/step-shaping.ts +6 -2
  163. package/src/runtime/query/prepare-turn.ts +18 -1
  164. package/src/runtime/query/project-instructions.ts +119 -38
  165. package/src/runtime/query/prompt.ts +11 -54
  166. package/src/runtime/query/resume-pending.ts +131 -0
  167. package/src/runtime/query/review-policy.ts +28 -0
  168. package/src/runtime/query/steering.ts +100 -96
  169. package/src/tools/builtins/bash.ts +13 -19
  170. package/src/tools/builtins/browser.ts +38 -12
  171. package/src/tools/builtins/edit.ts +4 -4
  172. package/src/tools/builtins/job.ts +1 -1
  173. package/src/tools/builtins/skill.ts +1 -1
  174. package/src/tools/builtins/structuredOutput.ts +1 -1
  175. package/src/tools/builtins/verify-outputs.ts +1 -1
  176. package/src/tools/builtins/write-file.ts +4 -4
  177. package/src/tools/coordinator/ask-user-question.ts +12 -1
  178. package/src/tools/coordinator/index.ts +10 -3
  179. package/src/tools/schedules/loop-tool.ts +4 -2
  180. package/src/tools/schedules/schedule-tool.ts +2 -2
  181. package/src/tools/task/create.ts +1 -1
  182. package/src/tools/untrusted-envelope.ts +1 -1
  183. package/src/turn/evidence-query.ts +19 -1
  184. package/src/turn/evidence-recall.ts +1 -1
  185. package/src/types/agent/base.ts +8 -0
  186. package/src/types/hitl/index.ts +67 -0
  187. package/src/types/message/index.ts +20 -2
  188. package/src/types/message/notice-frame.ts +88 -0
@@ -786,6 +786,15 @@ export interface QueryParams {
786
786
  * See {@link ReminderSource}.
787
787
  */
788
788
  reminders?: import('./reminders.js').ReminderSource
789
+ /**
790
+ * Whether the kernel tells the model, once, that the approval policy
791
+ * changed ("Approval policy changed from X to Y ..."), as request-only
792
+ * context after the history. Default `true`. A host that already tells the
793
+ * model about a mode change itself (through `reminders`) sets `false` so
794
+ * the model hears it once; the `approval_policy_changed` event is
795
+ * recorded either way.
796
+ */
797
+ announceApprovalPolicyChange?: boolean
789
798
 
790
799
  /**
791
800
  * Wake a background-task hold when operator input is available, without draining it.
@@ -840,6 +849,16 @@ export interface QueryParams {
840
849
  */
841
850
  reviewAllowedCalls?: () => boolean
842
851
 
852
+ /**
853
+ * Ask about one call at a time while the batch runs, instead of reviewing the
854
+ * whole batch as a unit. Opt-in; absent, nothing about review changes. See
855
+ * {@link import('../../types/hitl/index.js').PerCallReviewConfig}.
856
+ *
857
+ * Only this turn is affected: a delegated child reviews its own batches as
858
+ * it did before.
859
+ */
860
+ perCallReview?: import('../../types/hitl/index.js').PerCallReviewConfig
861
+
843
862
  /**
844
863
  * A name for the policy `resumeHandler` implements.
845
864
  *
@@ -1770,7 +1789,11 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
1770
1789
  // Read through the box on every call, so a swap lands on the next
1771
1790
  // question rather than the next turn.
1772
1791
  resumeHandler: (request, context) => approvalPolicy.current.handler(request, context),
1773
- takeApprovalPolicyChange: () => approvalPolicy.takeUnannouncedChange(),
1792
+ takeApprovalPolicyChange: () => {
1793
+ // Taken either way, so the change is not left pending for a later turn.
1794
+ const change = approvalPolicy.takeUnannouncedChange()
1795
+ return params.announceApprovalPolicyChange === false ? undefined : change
1796
+ },
1774
1797
  promptContributions,
1775
1798
  ...(params.steering ? { steering: params.steering } : {}),
1776
1799
  ...(jobNotices ? { jobNotices } : {}),
@@ -1788,6 +1811,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
1788
1811
  toolGrants: new ToolGrantSet(),
1789
1812
  skillGrants,
1790
1813
  ...(params.reviewAllowedCalls ? { reviewAllowedCalls: params.reviewAllowedCalls } : {}),
1814
+ ...(params.perCallReview ? { perCallReview: params.perCallReview } : {}),
1791
1815
  // Turn-scoped for the same reason. A repeat count carried into a later
1792
1816
  // run is a claim about work nobody repeated, and a module-level map
1793
1817
  // would leak exactly that way.
@@ -2690,6 +2714,26 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
2690
2714
  })
2691
2715
  return null
2692
2716
  })
2717
+ // A decision about one call of a batch answered the batch: the
2718
+ // batch's other open questions are moot.
2719
+ if (pendingResume?.partial && params.pendingDecision) {
2720
+ const batchIds = new Set(
2721
+ pendingResume.response.message.toolCalls?.map((call) => call.id) ?? [],
2722
+ )
2723
+ await checkpointMgr
2724
+ .supersedeBatchParks(
2725
+ batchIds,
2726
+ resolvedCheckpointId,
2727
+ supersededByRecovery(params.pendingDecision),
2728
+ )
2729
+ .catch((err: unknown) => {
2730
+ ctx.log.error('Applied a pending decision but failed to clear its sibling parks', {
2731
+ [NAMZU.TURN_ID]: ctx.turnId,
2732
+ 'exception.message': err instanceof Error ? err.message : String(err),
2733
+ })
2734
+ return 0
2735
+ })
2736
+ }
2693
2737
  }
2694
2738
 
2695
2739
  yield* iterationOrchestrator.runLoop()
@@ -102,6 +102,7 @@ import {
102
102
  streamWithProviderRejectedImageRecovery,
103
103
  } from './provider-rejected-image.js'
104
104
  import { type MediaRemoval, RequestRepair } from './request-repair.js'
105
+ import { placeBeforeLatestOperatorMessage } from './step-context.js'
105
106
  import {
106
107
  type StepShaping,
107
108
  appendWorkContext,
@@ -853,8 +854,8 @@ export class IterationOrchestrator {
853
854
  // there is no length check here — a second guard for the same
854
855
  // case is one more thing to keep in agreement with the first.
855
856
  const stepSkills = step.skills ? renderSkillsSection([...step.skills]) : null
856
- // A supervision change rides the same ephemeral slot, and for
857
- // the same reason: it applies to what happens next, not to the
857
+ // A supervision change is request-only context, and for the
858
+ // same reason: it applies to what happens next, not to the
858
859
  // run's history. The model plans around how closely it is being
859
860
  // watched — a turn that silently stops asking a human leaves it
860
861
  // batching destructive calls it expects to be reviewed, and one
@@ -879,7 +880,7 @@ export class IterationOrchestrator {
879
880
 
880
881
  // Request-only context: observations that need no system
881
882
  // authority and change from request to request. They ride
882
- // runtime-context messages of kind `step-context` after the
883
+ // runtime-context messages of kind `step-context`, most of them after the
883
884
  // history, which every driver keeps there and a caching driver
884
885
  // ends its breakpoint before — so a changed pin or a new turn
885
886
  // snapshot costs its own tokens, not a re-read of the history.
@@ -887,15 +888,27 @@ export class IterationOrchestrator {
887
888
  // (compaction preserves it there) and leaves the request's
888
889
  // system run here.
889
890
  const workingMemory = splitWorkingMemoryForRequest(baseMessages)
890
- const stepPreamble = [step.system, stepSkills, policyNotice, ...turnSections]
891
+ const stepPreamble = [step.system, stepSkills, ...turnSections]
891
892
  .filter(Boolean)
892
893
  .join('\n\n')
893
894
  const requestHistory = stepPreamble
894
895
  ? [...workingMemory.history, createSystemMessage(stepPreamble)]
895
896
  : [...workingMemory.history]
896
897
  if (workingMemory.context) requestHistory.push(workingMemory.context)
897
- if (contextText) requestHistory.push(stepContextMessage(contextText))
898
- if (step.context) requestHistory.push(stepContextMessage(step.context))
898
+ // The blocks of data that belong to this turn (recalled memory and
899
+ // evidence, the context contributions) sit just before the
900
+ // operator's latest message, so the request is the last thing the
901
+ // model reads. The short reminders and notices stay after it.
902
+ placeBeforeLatestOperatorMessage(
903
+ requestHistory,
904
+ [contextText, step.context]
905
+ .filter((text): text is string => Boolean(text))
906
+ .map(stepContextMessage),
907
+ )
908
+ // After the history, never ahead of it: a system message in front
909
+ // of the conversation moves the prefix every cached request shares,
910
+ // and a mode change would re-key the whole conversation.
911
+ if (policyNotice) requestHistory.push(stepContextMessage(policyNotice))
899
912
  const messages = projectRequestRichContent(
900
913
  this.projectObservations(requestHistory),
901
914
  this.ctx.turnConfig.maxRequestRichContentBytes ??
@@ -201,6 +201,8 @@ export interface IterationContext {
201
201
  readonly skillGrants?: SkillGrantSet
202
202
  /** See `QueryParams.reviewAllowedCalls`. Absent: allowed and granted batches skip review. */
203
203
  readonly reviewAllowedCalls?: () => boolean
204
+ /** See `QueryParams.perCallReview`. Absent: a batch is reviewed as a unit. */
205
+ readonly perCallReview?: import('../../../../types/hitl/index.js').PerCallReviewConfig
204
206
  /**
205
207
  * Absent when the host opted out with `repeatCallAdvisory: false`. The
206
208
  * opt-out is the ABSENCE, not a flag read at every call site, so a code
@@ -316,9 +318,10 @@ export async function awaitDecisionDurably(
316
318
  ctx: IterationContext,
317
319
  checkpoint: { readonly id: CheckpointId },
318
320
  request: Parameters<ResumeHandler>[0],
321
+ scope?: AskScope,
319
322
  ): Promise<HITLResumeDecision> {
320
323
  const delay = ctx.parkRecordDelayMs ?? PARK_RECORD_DELAY_MS
321
- const decisionPromise = awaitDecisionOrAbort(ctx, request)
324
+ const decisionPromise = awaitDecisionOrAbort(ctx, request, scope)
322
325
 
323
326
  let settled = false
324
327
  let recorded = false
@@ -423,9 +426,26 @@ export async function awaitDecisionDurably(
423
426
  * turns into `setStopReason('cancelled') + markCancelled + stop`. Fails closed:
424
427
  * a resume-handler rejection also resolves to `abort` rather than hanging.
425
428
  */
429
+ /**
430
+ * One question among several that are open at once (the per-call review).
431
+ *
432
+ * `signal` closes just this question; when it aborts and the turn itself was not
433
+ * cancelled, `closedAs` says what the question's answer is, so the park is
434
+ * recorded as what really ended it rather than as a cancelled turn.
435
+ */
436
+ export interface AskScope {
437
+ readonly signal: AbortSignal
438
+ readonly closedAs: () => HITLResumeDecision
439
+ }
440
+
426
441
  /** What a resume handler may read besides the request; see {@link ResumeContext}. */
427
- function resumeContextOf(ctx: IterationContext): ResumeContext {
428
- const signal = ctx.abortController?.signal
442
+ function resumeContextOf(ctx: IterationContext, scope?: AskScope): ResumeContext {
443
+ const turnSignal = ctx.abortController?.signal
444
+ const signal = scope
445
+ ? turnSignal
446
+ ? AbortSignal.any([turnSignal, scope.signal])
447
+ : scope.signal
448
+ : turnSignal
429
449
  return {
430
450
  transcript: () => ctx.recorder.messages,
431
451
  ...(signal ? { signal } : {}),
@@ -435,24 +455,30 @@ function resumeContextOf(ctx: IterationContext): ResumeContext {
435
455
  export async function awaitDecisionOrAbort(
436
456
  ctx: IterationContext,
437
457
  request: Parameters<ResumeHandler>[0],
458
+ scope?: AskScope,
438
459
  ): Promise<HITLResumeDecision> {
439
- const signal = ctx.abortController?.signal
460
+ const turnSignal = ctx.abortController?.signal
461
+ const signal = scope
462
+ ? turnSignal
463
+ ? AbortSignal.any([turnSignal, scope.signal])
464
+ : scope.signal
465
+ : turnSignal
440
466
  // No abort signal wired (e.g. a minimal test harness) → behave exactly as a
441
467
  // direct resumeHandler await, no race. In production TurnContextFactory always
442
468
  // provides the controller, so the race below is live.
443
- const resumeContext = resumeContextOf(ctx)
469
+ const resumeContext = resumeContextOf(ctx, scope)
444
470
  if (!signal) return ctx.resumeHandler(request, resumeContext)
445
- const abortDecision: HITLResumeDecision = {
446
- action: 'abort',
447
- reason: 'run aborted while parked for HITL',
448
- }
449
- if (signal.aborted) return abortDecision
471
+ const abortedDecision = (): HITLResumeDecision =>
472
+ scope && !turnSignal?.aborted
473
+ ? scope.closedAs()
474
+ : { action: 'abort', reason: 'run aborted while parked for HITL' }
475
+ if (signal.aborted) return abortedDecision()
450
476
  return new Promise<HITLResumeDecision>((resolve) => {
451
477
  let settled = false
452
478
  const onAbort = (): void => {
453
479
  if (settled) return
454
480
  settled = true
455
- resolve(abortDecision)
481
+ resolve(abortedDecision())
456
482
  }
457
483
  signal.addEventListener('abort', onAbort, { once: true })
458
484
  Promise.resolve(ctx.resumeHandler(request, resumeContext)).then(