melchizedek-agents 1.1.0 → 1.3.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 (146) hide show
  1. package/.env.example +19 -0
  2. package/AGENT_SETUP.md +3 -1
  3. package/CHANGELOG.md +373 -0
  4. package/DOCUMENTATION.md +193 -21
  5. package/ONBOARDING.md +378 -0
  6. package/QUICKSTART.md +15 -2
  7. package/README.md +26 -3
  8. package/config/agents/examples/connectors.yaml +82 -0
  9. package/config/agents/examples/council.yaml +1 -1
  10. package/config/agents/examples/critic.yaml +8 -8
  11. package/config/agents/examples/image_production.yaml +4 -3
  12. package/config/agents/syndicate.schema.json +389 -33
  13. package/config/agents/syndicateSchema.yaml +18 -6
  14. package/config/agents/templates/intake_extractor.yaml +3 -2
  15. package/config/agents/templates/systems_operator.yaml +5 -2
  16. package/dist/lib/a2a/app.d.ts +12 -0
  17. package/dist/lib/a2a/app.js +25 -11
  18. package/dist/lib/a2a/executor.js +14 -6
  19. package/dist/lib/a2a/oauthSetup.d.ts +16 -0
  20. package/dist/lib/a2a/oauthSetup.js +77 -8
  21. package/dist/lib/agentDialect.d.ts +18 -8
  22. package/dist/lib/agentDialect.js +36 -19
  23. package/dist/lib/chatgpt/adapter.d.ts +50 -0
  24. package/dist/lib/chatgpt/adapter.js +107 -0
  25. package/dist/lib/chatgpt/cli.d.ts +23 -0
  26. package/dist/lib/chatgpt/cli.js +77 -0
  27. package/dist/lib/chatgpt/oauth.d.ts +103 -0
  28. package/dist/lib/chatgpt/oauth.js +448 -0
  29. package/dist/lib/chatgpt/state.d.ts +98 -0
  30. package/dist/lib/chatgpt/state.js +145 -0
  31. package/dist/lib/chatgpt/store.d.ts +24 -0
  32. package/dist/lib/chatgpt/store.js +94 -0
  33. package/dist/lib/compile.d.ts +38 -7
  34. package/dist/lib/compile.js +97 -62
  35. package/dist/lib/compileNative.d.ts +11 -3
  36. package/dist/lib/compileNative.js +18 -1
  37. package/dist/lib/dispatch.js +6 -2
  38. package/dist/lib/doctor.d.ts +107 -0
  39. package/dist/lib/doctor.js +217 -7
  40. package/dist/lib/loadSyndicate.d.ts +15 -0
  41. package/dist/lib/mcp/server.d.ts +195 -0
  42. package/dist/lib/mcp/server.js +564 -0
  43. package/dist/lib/memory/supabaseMemoryService.js +2 -2
  44. package/dist/lib/models/adapterResolver.js +6 -1
  45. package/dist/lib/models/capabilities.d.ts +9 -0
  46. package/dist/lib/models/capabilities.js +24 -0
  47. package/dist/lib/models/chatCompletionsAdapter.d.ts +11 -3
  48. package/dist/lib/models/chatCompletionsAdapter.js +19 -5
  49. package/dist/lib/models/claudeAdapter.js +4 -0
  50. package/dist/lib/models/contract.d.ts +5 -2
  51. package/dist/lib/models/gateway.d.ts +3 -2
  52. package/dist/lib/models/gateway.js +8 -4
  53. package/dist/lib/models/gatewayAdapter.d.ts +7 -1
  54. package/dist/lib/models/gatewayAdapter.js +8 -0
  55. package/dist/lib/models/geminiAdapter.js +8 -2
  56. package/dist/lib/models/genaiMapping.d.ts +8 -4
  57. package/dist/lib/models/genaiMapping.js +23 -9
  58. package/dist/lib/models/gptAdapter.d.ts +4 -2
  59. package/dist/lib/models/gptAdapter.js +11 -4
  60. package/dist/lib/models/grokAdapter.d.ts +3 -1
  61. package/dist/lib/models/grokAdapter.js +11 -3
  62. package/dist/lib/models/kimiAdapter.d.ts +2 -1
  63. package/dist/lib/models/kimiAdapter.js +3 -3
  64. package/dist/lib/models/reasoning.d.ts +30 -2
  65. package/dist/lib/models/reasoning.js +56 -5
  66. package/dist/lib/onboarding.d.ts +135 -0
  67. package/dist/lib/onboarding.js +578 -0
  68. package/dist/lib/runtime/approvals.d.ts +2 -0
  69. package/dist/lib/runtime/approvals.js +4 -3
  70. package/dist/lib/runtime/credentials.d.ts +6 -0
  71. package/dist/lib/runtime/native/agentLoop.d.ts +5 -2
  72. package/dist/lib/runtime/native/agentLoop.js +2 -0
  73. package/dist/lib/runtime/native/checkpoint.d.ts +12 -5
  74. package/dist/lib/runtime/native/checkpoint.js +13 -19
  75. package/dist/lib/runtime/native/delegate.d.ts +82 -1
  76. package/dist/lib/runtime/native/delegate.js +157 -2
  77. package/dist/lib/runtime/native/interrupts.d.ts +88 -5
  78. package/dist/lib/runtime/native/interrupts.js +220 -30
  79. package/dist/lib/runtime/syndicateTurn.d.ts +4 -2
  80. package/dist/lib/runtime/syndicateTurn.js +324 -77
  81. package/dist/lib/runtime/turnControl.d.ts +41 -0
  82. package/dist/lib/session/transcript.d.ts +9 -0
  83. package/dist/lib/session/transcript.js +20 -1
  84. package/dist/lib/skills.d.ts +18 -0
  85. package/dist/lib/skills.js +73 -1
  86. package/dist/lib/storage/postgres/index.d.ts +6 -1
  87. package/dist/lib/storage/postgres/index.js +1 -1
  88. package/dist/lib/storage/postgres/taskQueue.d.ts +7 -3
  89. package/dist/lib/storage/postgres/taskQueue.js +10 -3
  90. package/dist/lib/syndicateSchema.d.ts +184 -9
  91. package/dist/lib/syndicateSchema.js +143 -29
  92. package/dist/lib/tools/auth.d.ts +13 -1
  93. package/dist/lib/tools/auth.js +10 -2
  94. package/dist/lib/tools/credentialHosts.d.ts +88 -0
  95. package/dist/lib/tools/credentialHosts.js +165 -0
  96. package/dist/lib/tools/credentialUses.d.ts +98 -0
  97. package/dist/lib/tools/credentialUses.js +120 -0
  98. package/dist/lib/tools/generateImageTool.js +2 -1
  99. package/dist/lib/tools/inspectImageTool.js +1 -1
  100. package/dist/lib/tools/mcpToolFactory.d.ts +29 -1
  101. package/dist/lib/tools/mcpToolFactory.js +72 -26
  102. package/dist/lib/tools/mcpTransports.d.ts +12 -0
  103. package/dist/lib/tools/mcpTransports.js +11 -0
  104. package/dist/lib/tools/oauthConsent.d.ts +18 -3
  105. package/dist/lib/tools/oauthConsent.js +57 -15
  106. package/dist/lib/tools/oauthDiscovery.d.ts +202 -0
  107. package/dist/lib/tools/oauthDiscovery.js +616 -0
  108. package/dist/lib/tools/oauthHosts.d.ts +2 -0
  109. package/dist/lib/tools/oauthHosts.js +2 -2
  110. package/dist/lib/tools/oauthTools.d.ts +54 -13
  111. package/dist/lib/tools/oauthTools.js +106 -23
  112. package/dist/lib/tools/openapiTools.d.ts +7 -0
  113. package/dist/lib/tools/openapiTools.js +49 -3
  114. package/dist/lib/tools/taskTools.d.ts +32 -2
  115. package/dist/lib/tools/taskTools.js +93 -33
  116. package/dist/lib/tools/xApiSearchTool.js +2 -2
  117. package/dist/lib/workflow/turn.d.ts +22 -3
  118. package/dist/lib/workflow/turn.js +151 -31
  119. package/dist/lib/workflow.d.ts +10 -7
  120. package/dist/lib/workflow.js +10 -7
  121. package/dist/scripts/a2a_server.d.ts +2 -2
  122. package/dist/scripts/a2a_server.js +19 -2
  123. package/dist/scripts/assistant_worker.d.ts +2 -0
  124. package/dist/scripts/assistant_worker.js +24 -5
  125. package/dist/scripts/doctor.d.ts +4 -2
  126. package/dist/scripts/doctor.js +6 -4
  127. package/dist/scripts/mcp_server.d.ts +12 -0
  128. package/dist/scripts/mcp_server.js +345 -0
  129. package/dist/scripts/setup.d.ts +25 -0
  130. package/dist/scripts/setup.js +181 -0
  131. package/dist/scripts/skills_install.d.ts +3 -1
  132. package/dist/scripts/skills_install.js +17 -2
  133. package/dist/scripts/syndicate_chat.js +12 -3
  134. package/dist/scripts/yaml_codemod.d.ts +12 -2
  135. package/dist/scripts/yaml_codemod.js +115 -33
  136. package/package.json +8 -2
  137. package/skills/README.md +11 -2
  138. package/skills/briefs/_shared.md +1 -1
  139. package/skills/briefs/melchizedek-models.md +4 -2
  140. package/skills/briefs/skills-readme.md +3 -1
  141. package/skills/melchizedek-models/SKILL.md +6 -2
  142. package/skills/melchizedek-onboard/SKILL.md +71 -0
  143. package/skills/melchizedek-onboard-cloud/SKILL.md +38 -0
  144. package/skills/melchizedek-onboard-keys/SKILL.md +47 -0
  145. package/skills/melchizedek-onboard-local/SKILL.md +41 -0
  146. package/skills/melchizedek-onboard-serve/SKILL.md +43 -0
@@ -104,6 +104,38 @@
104
104
  * asked at the end of the path. The walk stops there: a pause inside a
105
105
  * workflow node's own delegation does not run (lib/workflow/agentNode.ts).
106
106
  *
107
+ * NESTED WORKFLOWS AS A ROUTE OR A NODE (ADR 0119). A workflow node that is
108
+ * a nested workflow raises its walk's open requests again on its caller's
109
+ * walk, on one event of its own (lib/workflow/turn.ts), so the caller's
110
+ * walk pauses on the same ids; deepestPause follows such a request down,
111
+ * by id, through each child session filed under entryAppName (its kind in
112
+ * the key: `route:`, `node:`, ADR 0120; else an older key, legacyEntry) to
113
+ * the node that asked. A dispatch route that is one leaves its pause
114
+ * record as the conversation's last event (routePause). A delegated nested
115
+ * workflow's node that is one is followed the same way.
116
+ *
117
+ * NESTED DISPATCH SYNDICATES (ADR 0120). One runs as a turn of its own on
118
+ * its own conversation (the child session of a delegated call, a route or
119
+ * a node), its routes answering there as a turn's routes answer in the
120
+ * turn's conversation. conversationPause finds what it waits on as the
121
+ * turn finds it at the top: a route's pause record, a route's open
122
+ * request, question or consent request, else a pause inside a call a route
123
+ * left open (filed `<conversation>/<route>/<subagent>`). pauseBelow falls
124
+ * to it when the subagent's own checks find nothing; nestedWorkflowPause
125
+ * when the child session is not a walk. The answer goes down the open
126
+ * call, the route's pause record or the node's raised request, and the
127
+ * nested turn reads it as a message at the top.
128
+ *
129
+ * CONSENT AND SKILL SCRIPTS INSIDE DELEGATED SUBAGENTS (ADR 0118). A skill
130
+ * script run asks for approval through the same `adk_request_confirmation`
131
+ * call a gated tool stores, so the walk and the resume above carry it with
132
+ * nothing of its own. An OAuth consent request (`adk_request_credential`)
133
+ * the subagent left open in its child session is a pause too: the walk
134
+ * reports it with the path (pendingConsent), the turn runner stores the
135
+ * grant's answer once the callback has stored the grant, and the open
136
+ * call carries that answer down as it carries an approval decision; the
137
+ * child's own grantedCalls binds it and runs its paused call again.
138
+ *
107
139
  * ADK stays out of this file: an ADK tool an agent still lists is asked
108
140
  * whether it gates through its own checkRequireConfirmation, by shape.
109
141
  */
@@ -111,10 +143,11 @@ import { isDeepStrictEqual } from 'node:util';
111
143
  import { nativeToolOf } from '../../models/schemaNormalize.js';
112
144
  import { instructionToolOf, isTool, toolOf } from '../../tools/tool.js';
113
145
  import { inputRequestFrom } from '../../workflowConfig.js';
114
- import { pendingApproval } from '../approvals.js';
146
+ import { approvalRequestOf, pendingApproval } from '../approvals.js';
147
+ import { pendingConsent } from '../credentials.js';
115
148
  import { getFunctionCalls, getFunctionResponses } from '../events.js';
116
149
  import { ASK_USER, pendingQuestion } from '../questions.js';
117
- import { childAppName, legacyChild } from './delegate.js';
150
+ import { childAppName, entryAppName, legacyChild, legacyEntry } from './delegate.js';
118
151
  import { REQUEST_CONFIRMATION_CALL, REQUEST_CREDENTIAL_CALL, REQUEST_INPUT_CALL, isSegmentPrefix } from './history.js';
119
152
  import { isToolset } from './request.js';
120
153
  /** A confirmation that does not bind to its pinned call: ADK's IntentMismatchError, by name and text. */
@@ -386,9 +419,9 @@ async function pauseBelow(key, caller, call, depth, seen) {
386
419
  if (!child)
387
420
  return undefined;
388
421
  const here = [caller, name];
389
- const walked = workflowPause(child.events, name);
422
+ const walked = await workflowPauseIn(key, child, name);
390
423
  if (walked) {
391
- const path = [...here, walked.asker];
424
+ const path = [caller, ...walked.path];
392
425
  return {
393
426
  path,
394
427
  callIds: [id],
@@ -402,50 +435,206 @@ async function pauseBelow(key, caller, call, depth, seen) {
402
435
  const question = pendingQuestion(child.events);
403
436
  if (question && question.node === name)
404
437
  return { path: here, callIds: [id], question: { ...question, path: here } };
438
+ // Only the subagent's own request counts (pendingConsent already skips a user-authored one).
439
+ const consent = pendingConsent(child.events);
440
+ if (consent && consent.agent === name)
441
+ return { path: here, callIds: [id], consent: { ...consent, path: here } };
405
442
  const [first] = await delegatedPauses({ ...key, appName: child.appName, delegated: true }, child.events, name, depth + 1, new Set([...seen, name]));
443
+ if (first) {
444
+ const path = [caller, ...first.path];
445
+ return {
446
+ path,
447
+ callIds: [id, ...first.callIds],
448
+ ...(first.approval ? { approval: { ...first.approval, path } } : {}),
449
+ ...(first.question ? { question: { ...first.question, path } } : {}),
450
+ ...(first.consent ? { consent: { ...first.consent, path } } : {}),
451
+ };
452
+ }
453
+ // A nested dispatch syndicate delegated to (ADR 0120): its routes answer in the child session, as routes answer in a turn's.
454
+ const routed = await conversationPause(key, child, name, undefined, depth + 1, new Set([...seen, name]));
455
+ if (!routed)
456
+ return undefined;
457
+ const path = [caller, ...routed.path];
458
+ return {
459
+ path,
460
+ callIds: [id],
461
+ ...(routed.approval ? { approval: { ...routed.approval, path } } : {}),
462
+ ...(routed.question ? { question: { ...routed.question, path } } : {}),
463
+ ...(routed.consent ? { consent: { ...routed.consent, path } } : {}),
464
+ };
465
+ }
466
+ /** The id a pause found below waits on. */
467
+ const idOf = (pause) => pause.approval?.id ?? pause.question?.id ?? pause.consent?.id;
468
+ /**
469
+ * What a nested dispatch syndicate's conversation (`child`, filed for the
470
+ * entry `name`) waits on (ADR 0120): what its own turn would report at the
471
+ * top. Its routes answer there as a turn's routes answer in the turn's
472
+ * conversation, so it is found as the turn finds it: a route's pause record
473
+ * (a nested workflow or a nested dispatch syndicate run as a route, followed
474
+ * down), a route's open approval request, question or consent request, else
475
+ * a pause inside a call a route left open, its child filed under the
476
+ * route's name (`<child>/<route>/<subagent>`, as a turn's route files its
477
+ * own). The path runs from `name` down to the agent that asked. With `id`,
478
+ * only that request counts. Calls `name` made itself are not a route's.
479
+ */
480
+ async function conversationPause(key, child, name, id, depth, seen = new Set()) {
481
+ if (depth >= MAX_DELEGATION_DEPTH)
482
+ return undefined;
483
+ const at = { ...key, appName: child.appName, delegated: false };
484
+ const wanted = (found) => !!found && (id === undefined || found === id);
485
+ const down = (path) => [name, ...path];
486
+ const routed = await routePause(at, child.events, undefined, depth + 1);
487
+ if (routed && wanted(idOf(routed))) {
488
+ const path = down(routed.path);
489
+ return {
490
+ path,
491
+ ...(routed.approval ? { approval: { ...routed.approval, path } } : {}),
492
+ ...(routed.question ? { question: { ...routed.question, path } } : {}),
493
+ ...(routed.consent ? { consent: { ...routed.consent, path } } : {}),
494
+ };
495
+ }
496
+ const approval = pendingApproval(child.events);
497
+ if (approval && approval.agent && approval.agent !== 'user' && approval.agent !== name && wanted(approval.id)) {
498
+ const path = down([approval.agent]);
499
+ return { path, approval: { ...approval, path } };
500
+ }
501
+ const question = pendingQuestion(child.events);
502
+ if (question && question.node && question.node !== 'user' && question.node !== name && wanted(question.id)) {
503
+ const path = down([question.node]);
504
+ return { path, question: { ...question, path } };
505
+ }
506
+ const consent = pendingConsent(child.events);
507
+ if (consent && consent.agent && consent.agent !== name && wanted(consent.id)) {
508
+ const path = down([consent.agent]);
509
+ return { path, consent: { ...consent, path } };
510
+ }
511
+ const below = await delegatedPauses(at, child.events, undefined, depth + 1, seen);
512
+ const first = below.find((p) => p.path[0] !== name && wanted(interruptIdOf(p)));
406
513
  if (!first)
407
514
  return undefined;
408
- const path = [caller, ...first.path];
515
+ const path = down(first.path);
409
516
  return {
410
517
  path,
411
- callIds: [id, ...first.callIds],
412
518
  ...(first.approval ? { approval: { ...first.approval, path } } : {}),
413
519
  ...(first.question ? { question: { ...first.question, path } } : {}),
520
+ ...(first.consent ? { consent: { ...first.consent, path } } : {}),
414
521
  };
415
522
  }
523
+ /** A walk's session: its node events carry a dotted node path (`<workflow>.<node>`). */
524
+ const isWalk = (session) => session.events.some((e) => typeof e.nodeInfo?.path === 'string' && e.nodeInfo.path.includes('.'));
416
525
  /**
417
526
  * The pause a nested workflow's walk holds in its child session (ADR 0111):
418
527
  * the workflow's own pause record, the last event, authored by the
419
528
  * workflow (`name`) at its own path with no content, names the open
420
529
  * interrupts; the approval request or ask_user question among them is the
421
- * pause, and the node that raised it the asker. Undefined when the session
422
- * holds no paused walk of `name`'s.
530
+ * pause, and the node that raised it the asker. With `id`, only that
531
+ * interrupt counts. Undefined when the session holds no paused walk of
532
+ * `name`'s. A node that is itself a nested workflow raised its walk's
533
+ * requests again on its own event (lib/workflow/turn.ts, ADR 0119), so it
534
+ * is the asker here, and workflowPauseIn goes on down.
423
535
  */
424
- function workflowPause(events, name) {
536
+ function workflowPause(events, name, id) {
425
537
  const record = events[events.length - 1];
426
538
  if (!record || record.author !== name || (record.content?.parts ?? []).length > 0 || record.nodeInfo?.path !== name)
427
539
  return undefined;
428
- const open = new Set(record.longRunningToolIds ?? []);
540
+ const open = new Set((record.longRunningToolIds ?? []).filter((x) => id === undefined || x === id));
429
541
  if (open.size === 0)
430
542
  return undefined;
431
- const approval = pendingApproval(events);
432
- if (approval && open.has(approval.id) && approval.agent && approval.agent !== 'user')
433
- return { asker: approval.agent, approval };
434
- // The ask_user node's request the record names: the walk stores node inputs as user turns, so a later one may follow it.
435
- for (let i = events.length - 2; i >= 0; i--) {
436
- const event = events[i];
437
- if (!event.author || event.author === 'user')
438
- continue;
439
- for (const call of getFunctionCalls(event)) {
440
- if (call.name !== REQUEST_INPUT_CALL || !call.id || !open.has(call.id))
543
+ // The latest open request the record names, an approval first: the walk stores node inputs as user turns, so a later one may follow it.
544
+ for (const kind of [REQUEST_CONFIRMATION_CALL, REQUEST_INPUT_CALL]) {
545
+ for (let i = events.length - 2; i >= 0; i--) {
546
+ const event = events[i];
547
+ if (!event.author || event.author === 'user')
441
548
  continue;
442
- const question = inputRequestFrom(event.author, call);
443
- if (question && question.id === call.id)
444
- return { asker: event.author, question };
549
+ for (const call of getFunctionCalls(event)) {
550
+ if (call.name !== kind || !call.id || !open.has(call.id))
551
+ continue;
552
+ if (kind === REQUEST_CONFIRMATION_CALL)
553
+ return { asker: event.author, approval: approvalRequestOf(event, call) };
554
+ const question = inputRequestFrom(event.author, call);
555
+ if (question && question.id === call.id)
556
+ return { asker: event.author, question };
557
+ }
445
558
  }
446
559
  }
447
560
  return undefined;
448
561
  }
562
+ /**
563
+ * The pause the walk of `name` holds in `child` (its child session), with
564
+ * `id` the only interrupt that counts when given: the node that asked, or,
565
+ * when that node is itself a nested workflow (ADR 0119), the pause its own
566
+ * child session holds under the same id, one level further down.
567
+ */
568
+ async function workflowPauseIn(key, child, name, id, depth = 0) {
569
+ const walked = workflowPause(child.events, name, id);
570
+ if (!walked)
571
+ return undefined;
572
+ const found = (walked.approval?.id ?? walked.question?.id);
573
+ const deeper = await nestedWorkflowPause({ ...key, appName: child.appName }, walked.asker, 'node', found, depth + 1);
574
+ const path = deeper ? [name, ...deeper.path] : [name, walked.asker];
575
+ const approval = deeper ? deeper.approval : walked.approval;
576
+ const question = deeper ? deeper.question : walked.question;
577
+ return { path, ...(approval ? { approval: { ...approval, path } } : {}), ...(question ? { question: { ...question, path } } : {}) };
578
+ }
579
+ /**
580
+ * The pause a nested workflow run as a dispatch route or a workflow node
581
+ * holds (ADR 0119): `name`'s child session below the session filed under
582
+ * `key.appName` (delegate.ts entryAppName), else the one filed under its
583
+ * name alone (legacyChild), and the paused walk there, followed down to the
584
+ * node that asked. With `id`, only that interrupt counts: a request a node
585
+ * raised again on its caller's walk names the one its own walk waits on.
586
+ */
587
+ export async function nestedWorkflowPause(key, name, kind, id, depth = 0) {
588
+ if (depth >= MAX_DELEGATION_DEPTH)
589
+ return undefined;
590
+ const own = await key.sessions.get({ appName: entryAppName(key.appName, name, kind), userId: key.userId, sessionId: key.sessionId });
591
+ const child = own ?? (await legacyEntry(key.sessions, key, name, true));
592
+ if (!child)
593
+ return undefined;
594
+ const walked = await workflowPauseIn(key, child, name, id, depth);
595
+ if (walked || isWalk(child))
596
+ return walked;
597
+ // A nested dispatch syndicate run as the route or the node (ADR 0120): its own conversation, as its turn reads it.
598
+ return conversationPause(key, child, name, id, depth + 1);
599
+ }
600
+ /**
601
+ * A dispatch route that is a nested workflow, paused (ADR 0119): the
602
+ * conversation's last event is the route's pause record (authored by the
603
+ * route at its own path, no content, the walk's open interrupts in
604
+ * `longRunningToolIds`; lib/runtime/syndicateTurn.ts stores it), and the
605
+ * route's child session holds the paused walk under one of those ids.
606
+ * Anything stored after the record closes it. `routes`, when given, are the
607
+ * names a record may carry.
608
+ */
609
+ export async function routePause(key, events, routes, depth = 0) {
610
+ const record = events[events.length - 1];
611
+ const route = record?.author;
612
+ if (!record || !route || route === 'user' || (record.content?.parts ?? []).length > 0 || record.nodeInfo?.path !== route)
613
+ return undefined;
614
+ if (routes && !routes.has(route))
615
+ return undefined;
616
+ for (const id of record.longRunningToolIds ?? []) {
617
+ const pause = await nestedWorkflowPause(key, route, 'route', id, depth);
618
+ if (pause)
619
+ return { ...pause, route };
620
+ }
621
+ return undefined;
622
+ }
623
+ /**
624
+ * An approval request or question found in a workflow's own session, raised
625
+ * by a node that is a nested workflow (lib/workflow/turn.ts raises its
626
+ * walk's requests again on the node's event, ADR 0119): the one its walk
627
+ * waits on, with the path from the node down to the one that asked. Any
628
+ * other request comes back as it is.
629
+ */
630
+ export async function deepestPause(key, pending) {
631
+ const raisedBy = 'agent' in pending ? pending.agent : pending.node;
632
+ if (!raisedBy || pending.path)
633
+ return pending;
634
+ const below = await nestedWorkflowPause(key, raisedBy, 'node', pending.id);
635
+ const found = 'agent' in pending ? below?.approval : below?.question;
636
+ return found ?? pending;
637
+ }
449
638
  /**
450
639
  * Every pause waiting inside a delegated call still open in `events` (one
451
640
  * session's events), latest call first: each open call whose child session
@@ -462,18 +651,19 @@ export async function delegatedPauses(key, events, author, depth = 0, seen = new
462
651
  }
463
652
  return out;
464
653
  }
465
- /** The interrupt a delegated pause waits on: the approval request's id, or the question's. */
654
+ /** The interrupt a delegated pause waits on: the approval request's id, the question's, or the consent request's. */
466
655
  export function interruptIdOf(pause) {
467
- return pause.approval?.id ?? pause.question?.id;
656
+ return pause.approval?.id ?? pause.question?.id ?? pause.consent?.id;
468
657
  }
469
658
  /**
470
659
  * The delegated calls `agent` left open that the latest user message
471
660
  * answers (ADR 0110): the message's approval decisions and question
472
661
  * answers (function responses to `adk_request_confirmation` or `ask_user`)
473
- * matched, by interrupt id, to the pause below each open call. Undefined
474
- * when the message answers none. The child binds the answer itself: an
475
- * approval is checked against its pinned call there (approvedCalls), as at
476
- * the top.
662
+ * and grants (to `adk_request_credential`, ADR 0118) matched, by interrupt
663
+ * id, to the pause below each open call. Undefined when the message answers
664
+ * none. The child binds the answer itself: an approval is checked against
665
+ * its pinned call there (approvedCalls), a grant against its request
666
+ * (grantedCalls), as at the top.
477
667
  */
478
668
  export async function resumedDelegations(agent, scope, sessions) {
479
669
  const events = scope.session.events;
@@ -485,7 +675,7 @@ export async function resumedDelegations(agent, scope, sessions) {
485
675
  return undefined;
486
676
  const answerParts = (latest.content?.parts ?? []).filter((p) => {
487
677
  const r = p.functionResponse;
488
- return !!r?.id && (r.name === REQUEST_CONFIRMATION_CALL || r.name === ASK_USER);
678
+ return !!r?.id && (r.name === REQUEST_CONFIRMATION_CALL || r.name === ASK_USER || r.name === REQUEST_CREDENTIAL_CALL);
489
679
  });
490
680
  if (answerParts.length === 0)
491
681
  return undefined;
@@ -158,7 +158,8 @@ export interface SyndicateTurnOptions {
158
158
  * the native runtime. The store is pinned to `appName`; a tool reads the
159
159
  * grant of `userId` only. With `consent`, a call whose provider the user
160
160
  * has not granted pauses the turn input-required (`result.consent`), and
161
- * the next message after the grant resumes it. Omitted: tools have no
161
+ * the next message after the grant resumes it; inside a delegated
162
+ * subagent too, with `consent.path` set (ADR 0118). Omitted: tools have no
162
163
  * `accessToken`, as before.
163
164
  */
164
165
  toolCredentials?: ToolCredentials;
@@ -229,7 +230,8 @@ export interface SyndicateTurnResult {
229
230
  input?: PendingInput;
230
231
  /** The OAuth grant a paused call waits for, when status is input-required
231
232
  * (ADR 0085): the person opens `consent.authUri`; their next message after
232
- * the grant is stored resumes the call. */
233
+ * the grant is stored resumes the call. `consent.path` is set for a call
234
+ * inside a delegated subagent (ADR 0118). */
233
235
  consent?: PendingConsent;
234
236
  /** The text the user receives (relay fallback and guards applied). */
235
237
  text: string;