@adonis-agora/agent 0.55.0 → 0.57.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 (119) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/providers/agent_provider.d.ts.map +1 -1
  3. package/dist/providers/agent_provider.js +84 -10
  4. package/dist/providers/agent_provider.js.map +1 -1
  5. package/dist/src/ag-ui/adapter.d.ts.map +1 -1
  6. package/dist/src/ag-ui/adapter.js +1 -2
  7. package/dist/src/ag-ui/adapter.js.map +1 -1
  8. package/dist/src/ag-ui/encoder.d.ts +7 -92
  9. package/dist/src/ag-ui/encoder.d.ts.map +1 -1
  10. package/dist/src/ag-ui/encoder.js +14 -498
  11. package/dist/src/ag-ui/encoder.js.map +1 -1
  12. package/dist/src/ag-ui/frames.d.ts +17 -0
  13. package/dist/src/ag-ui/frames.d.ts.map +1 -0
  14. package/dist/src/ag-ui/frames.js +69 -0
  15. package/dist/src/ag-ui/frames.js.map +1 -0
  16. package/dist/src/ag-ui/index.d.ts +9 -6
  17. package/dist/src/ag-ui/index.d.ts.map +1 -1
  18. package/dist/src/ag-ui/index.js +9 -6
  19. package/dist/src/ag-ui/index.js.map +1 -1
  20. package/dist/src/ag-ui/stream.d.ts +7 -23
  21. package/dist/src/ag-ui/stream.d.ts.map +1 -1
  22. package/dist/src/ag-ui/stream.js +9 -64
  23. package/dist/src/ag-ui/stream.js.map +1 -1
  24. package/dist/src/agent-service.d.ts +22 -0
  25. package/dist/src/agent-service.d.ts.map +1 -1
  26. package/dist/src/agent-service.js +57 -5
  27. package/dist/src/agent-service.js.map +1 -1
  28. package/dist/src/ai-tool-ref.d.ts +26 -4
  29. package/dist/src/ai-tool-ref.d.ts.map +1 -1
  30. package/dist/src/ai-tool-ref.js +6 -1
  31. package/dist/src/ai-tool-ref.js.map +1 -1
  32. package/dist/src/confirmed-tool.d.ts +2 -2
  33. package/dist/src/confirmed-tool.d.ts.map +1 -1
  34. package/dist/src/confirmed-tool.js.map +1 -1
  35. package/dist/src/guardrails/agent.d.ts.map +1 -1
  36. package/dist/src/guardrails/agent.js +11 -0
  37. package/dist/src/guardrails/agent.js.map +1 -1
  38. package/dist/src/index.d.ts +4 -3
  39. package/dist/src/index.d.ts.map +1 -1
  40. package/dist/src/index.js +4 -3
  41. package/dist/src/index.js.map +1 -1
  42. package/dist/src/mcp-client/options.d.ts +12 -0
  43. package/dist/src/mcp-client/options.d.ts.map +1 -1
  44. package/dist/src/mcp-client/tool-source.d.ts.map +1 -1
  45. package/dist/src/mcp-client/tool-source.js +2 -0
  46. package/dist/src/mcp-client/tool-source.js.map +1 -1
  47. package/dist/src/media/index.d.ts +1 -1
  48. package/dist/src/media/index.d.ts.map +1 -1
  49. package/dist/src/media/index.js +1 -1
  50. package/dist/src/media/index.js.map +1 -1
  51. package/dist/src/media/media-attachment-staging.d.ts +107 -6
  52. package/dist/src/media/media-attachment-staging.d.ts.map +1 -1
  53. package/dist/src/media/media-attachment-staging.js +173 -21
  54. package/dist/src/media/media-attachment-staging.js.map +1 -1
  55. package/dist/src/react/ag-ui.d.ts +41 -0
  56. package/dist/src/react/ag-ui.d.ts.map +1 -0
  57. package/dist/src/react/ag-ui.js +63 -0
  58. package/dist/src/react/ag-ui.js.map +1 -0
  59. package/dist/src/react/client.d.ts +12 -0
  60. package/dist/src/react/client.d.ts.map +1 -0
  61. package/dist/src/react/client.js +21 -0
  62. package/dist/src/react/client.js.map +1 -0
  63. package/dist/src/react/index.d.ts +3 -8
  64. package/dist/src/react/index.d.ts.map +1 -1
  65. package/dist/src/react/index.js +4 -20
  66. package/dist/src/react/index.js.map +1 -1
  67. package/dist/src/react/media.d.ts +18 -0
  68. package/dist/src/react/media.d.ts.map +1 -0
  69. package/dist/src/react/media.js +18 -0
  70. package/dist/src/react/media.js.map +1 -0
  71. package/dist/src/spi/agent-store.d.ts +22 -0
  72. package/dist/src/spi/agent-store.d.ts.map +1 -1
  73. package/dist/src/spi/agent-store.js.map +1 -1
  74. package/dist/src/spi/attachment-staging.d.ts +5 -0
  75. package/dist/src/spi/attachment-staging.d.ts.map +1 -1
  76. package/dist/src/spi/tool.d.ts +24 -0
  77. package/dist/src/spi/tool.d.ts.map +1 -1
  78. package/dist/src/stores/factory.d.ts +8 -2
  79. package/dist/src/stores/factory.d.ts.map +1 -1
  80. package/dist/src/stores/factory.js +8 -2
  81. package/dist/src/stores/factory.js.map +1 -1
  82. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  83. package/dist/src/stores/lucid-schema.js +2 -0
  84. package/dist/src/stores/lucid-schema.js.map +1 -1
  85. package/dist/src/stores/lucid.d.ts +12 -0
  86. package/dist/src/stores/lucid.d.ts.map +1 -1
  87. package/dist/src/stores/lucid.js +47 -0
  88. package/dist/src/stores/lucid.js.map +1 -1
  89. package/dist/src/testing/in-memory-store.d.ts +7 -0
  90. package/dist/src/testing/in-memory-store.d.ts.map +1 -1
  91. package/dist/src/testing/in-memory-store.js +33 -0
  92. package/dist/src/testing/in-memory-store.js.map +1 -1
  93. package/dist/src/tool-discovery.d.ts.map +1 -1
  94. package/dist/src/tool-discovery.js +27 -9
  95. package/dist/src/tool-discovery.js.map +1 -1
  96. package/dist/src/tool-filters.d.ts +33 -0
  97. package/dist/src/tool-filters.d.ts.map +1 -0
  98. package/dist/src/tool-filters.js +45 -0
  99. package/dist/src/tool-filters.js.map +1 -0
  100. package/dist/src/tool-registry.d.ts +33 -5
  101. package/dist/src/tool-registry.d.ts.map +1 -1
  102. package/dist/src/tool-registry.js +55 -11
  103. package/dist/src/tool-registry.js.map +1 -1
  104. package/dist/src/types.d.ts +16 -0
  105. package/dist/src/types.d.ts.map +1 -1
  106. package/package.json +15 -5
  107. package/skills/agent-tools/SKILL.md +19 -1
  108. package/dist/src/ag-ui/input.d.ts +0 -72
  109. package/dist/src/ag-ui/input.d.ts.map +0 -1
  110. package/dist/src/ag-ui/input.js +0 -216
  111. package/dist/src/ag-ui/input.js.map +0 -1
  112. package/dist/src/ag-ui/interrupt-id.d.ts +0 -27
  113. package/dist/src/ag-ui/interrupt-id.d.ts.map +0 -1
  114. package/dist/src/ag-ui/interrupt-id.js +0 -42
  115. package/dist/src/ag-ui/interrupt-id.js.map +0 -1
  116. package/dist/src/ag-ui/types.d.ts +0 -202
  117. package/dist/src/ag-ui/types.d.ts.map +0 -1
  118. package/dist/src/ag-ui/types.js +0 -33
  119. package/dist/src/ag-ui/types.js.map +0 -1
@@ -1,107 +1,22 @@
1
+ import { type AgUiEncoderOptions, type AgUiEvent } from '@dudousxd/nestjs-agent-core/ag-ui';
1
2
  import type { StreamFrame } from '../spi/token-stream-sink.js';
2
- import { type AgUiEvent } from './types.js';
3
- export interface AgUiEncoderOptions {
4
- /** The ids the consumer sent on `RunAgentInput`: every boundary event echoes them. */
5
- threadId: string;
6
- runId: string;
7
- /** The library run whose stream is being projected (it mints interrupt ids and the `agora.run` event). */
8
- streamRunId: string;
9
- /** The library's thread id, when it differs from what the consumer calls the thread. */
10
- streamThreadId?: string;
11
- /**
12
- * Frames of the library run's stream that an earlier AG-UI run already delivered (a resume). They
13
- * are fed to the encoder like any other — it has to know what they opened — but produce no events.
14
- */
15
- skip?: number;
16
- /**
17
- * Tool calls a resuming request just answered. The run has not necessarily said so yet — a parked
18
- * run takes a moment to wake — so without this the encoder would read them as still waiting and
19
- * report the interrupt again before the answer had any effect.
20
- */
21
- answered?: readonly string[];
22
- /** Model id for the usage entry when the step frames carry none. */
23
- model?: string;
24
- }
3
+ export type { AgUiEncoderOptions };
25
4
  /**
26
- * Projects one library run's stream ({@link StreamFrame}s, in buffer order) onto AG-UI 1.0 events.
27
- *
28
- * A pure state machine: the same frames always give the same events, which is what lets a resuming
29
- * request — served by any replica — replay the run's buffer, skip what the interrupted AG-UI run
30
- * already delivered, and continue. It writes nothing itself; {@link agUiEvents} drives it and
31
- * decides when the run has stopped to ask.
32
- *
33
- * What maps where:
34
- * - `text` → `TEXT_MESSAGE_*` (one message per stretch of text);
35
- * - `reasoning` → a reasoning span with one reasoning message;
36
- * - `step-start`/`step-finish` → `STEP_STARTED`/`STEP_FINISHED`, the step's usage folded into the
37
- * run's `usage`;
38
- * - `tool-input-*` → `TOOL_CALL_START`/`ARGS`/`END`; `tool-output*` → `TOOL_CALL_RESULT`;
39
- * - an approval or a question set the run parks on → an `Interrupt` on the closing `RUN_FINISHED`;
40
- * - `cancelled` → the cancelled outcome; an error frame → `RUN_ERROR`;
41
- * - generative UI, title, queue and the native approval/elicitation frames → `CUSTOM` events named
42
- * in {@link AG_UI_CUSTOM}, for what AG-UI has no event for.
43
- *
44
- * Not mapped: sub-agent attribution (`subagentRunId`) — a delegated agent's frames are projected
45
- * flat, as the protocol allows of a producer that does not attribute.
5
+ * The shared AG-UI encoder (`@dudousxd/nestjs-agent-core/ag-ui`, one implementation for both
6
+ * servers), reading this package's {@link StreamFrame}s. A pure state machine: the same frames
7
+ * always give the same events.
46
8
  */
47
9
  export declare class AgUiEncoder {
48
- private readonly options;
49
- private position;
50
- private started;
51
- private closed;
52
- private cancelled;
53
- private crossed;
54
- private messageSeq;
55
- private stepSeq;
56
- private openText;
57
- private openReasoning;
58
- /** The last text message of the current step: a tool call attaches to it. */
59
- private stepMessage;
60
- private readonly openSteps;
61
- private readonly calls;
62
- private readonly pending;
63
- private readonly usage;
10
+ private readonly shared;
64
11
  constructor(options: AgUiEncoderOptions);
65
12
  /** How many frames of the library stream this encoder has consumed. */
66
13
  get consumed(): number;
67
14
  /** The run is waiting on at least one approval or question set nobody has settled yet. */
68
15
  get waiting(): boolean;
69
- /**
70
- * Is anything still in flight that is NOT waiting on a person — a tool call announced and neither
71
- * answered nor parked? While there is, more frames are coming without anyone's help.
72
- */
16
+ /** A tool call is announced and neither answered nor parked: more frames are coming. */
73
17
  get busy(): boolean;
74
- /** `RUN_STARTED`, and the library's own ids for the run. Call once, before the first frame. */
75
18
  start(): AgUiEvent[];
76
- /** The AG-UI events the next frame of the library stream stands for (possibly none). */
77
19
  encode(frame: StreamFrame): AgUiEvent[];
78
- /**
79
- * The replayed frames are behind us: from here on events are written. The AG-UI run that delivered
80
- * them closed everything it had open before it finished, so this run starts with nothing open, and
81
- * what the resuming request answered is no longer waiting.
82
- */
83
- private crossBoundary;
84
- private get replaying();
85
- private project;
86
- private event;
87
- /**
88
- * The events that end the AG-UI run: whatever is still open is closed first, then `RUN_FINISHED`
89
- * with how it ended — interrupted when the run is waiting on someone, cancelled when it was
90
- * stopped, success otherwise. Nothing after a `RUN_ERROR`. `ended` says the library run's stream
91
- * itself ended, rather than this reader having stopped following it.
92
- */
93
20
  finish(ended?: boolean): AgUiEvent[];
94
- private interrupts;
95
- private custom;
96
- private nextMessageId;
97
- private textDelta;
98
- private reasoningDelta;
99
- private closeText;
100
- private closeReasoning;
101
- private closeStreams;
102
- private callStart;
103
- private result;
104
- private addUsage;
105
- private usageEntries;
106
21
  }
107
22
  //# sourceMappingURL=encoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/ag-ui/encoder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAI/D,OAAO,EAGL,KAAK,SAAS,EAGf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,0GAA0G;IAC1G,WAAW,EAAE,MAAM,CAAC;IACpB,wFAAwF;IACxF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,oEAAoE;IACpE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA0DD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW;IAiBV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhBpC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAqB;IAC1C,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8B;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAE3D,YAA6B,OAAO,EAAE,kBAAkB,EAAI;IAE5D,uEAAuE;IACvE,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,0FAA0F;IAC1F,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;;OAGG;IACH,IAAI,IAAI,IAAI,OAAO,CAKlB;IAED,+FAA+F;IAC/F,KAAK,IAAI,SAAS,EAAE,CAmBnB;IAED,wFAAwF;IACxF,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAgBtC;IAED;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAarB,OAAO,KAAK,SAAS,GAEpB;IAED,OAAO,CAAC,OAAO;IA+Ef,OAAO,CAAC,KAAK;IAgHb;;;;;OAKG;IACH,MAAM,CAAC,KAAK,UAAQ,GAAG,SAAS,EAAE,CA+BjC;IAED,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,QAAQ;IAsBhB,OAAO,CAAC,YAAY;CAGrB"}
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/ag-ui/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAEf,MAAM,mCAAmC,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG/D,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,YAAY,OAAO,EAAE,kBAAkB,EAEtC;IAED,uEAAuE;IACvE,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,0FAA0F;IAC1F,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,wFAAwF;IACxF,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,KAAK,IAAI,SAAS,EAAE,CAEnB;IAED,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,SAAS,EAAE,CAEtC;IAED,MAAM,CAAC,KAAK,UAAQ,GAAG,SAAS,EAAE,CAEjC;CACF"}
@@ -1,519 +1,35 @@
1
- import { encodeInterruptId } from './interrupt-id.js';
2
- import { AG_UI_CUSTOM, AG_UI_PROTOCOL_VERSION, } from './types.js';
3
- const APPROVAL_SCHEMA = {
4
- type: 'object',
5
- properties: {
6
- approved: { type: 'boolean' },
7
- reason: { type: 'string' },
8
- remember: { type: 'boolean' },
9
- },
10
- required: ['approved'],
11
- };
12
- /** The answer an elicitation expects: `{ answers: { <questionId>: string[] } }`. */
13
- function elicitationSchema(request) {
14
- const properties = {};
15
- for (const question of request.questions) {
16
- const options = question.options?.map((option) => option.value);
17
- properties[question.id] = {
18
- type: 'array',
19
- title: question.prompt,
20
- ...(question.description !== undefined ? { description: question.description } : {}),
21
- items: options !== undefined && options.length > 0 && question.allowFreeText !== true
22
- ? { type: 'string', enum: options }
23
- : { type: 'string' },
24
- ...(question.multiple === true ? {} : { maxItems: 1 }),
25
- ...(question.defaults !== undefined ? { default: question.defaults } : {}),
26
- };
27
- }
28
- return {
29
- type: 'object',
30
- properties: { answers: { type: 'object', properties } },
31
- required: ['answers'],
32
- };
33
- }
34
- function text(value) {
35
- if (typeof value === 'string')
36
- return value;
37
- const json = JSON.stringify(value);
38
- return json === undefined ? '' : json;
39
- }
1
+ import { AgUiEncoder as SharedEncoder, } from '@dudousxd/nestjs-agent-core/ag-ui';
2
+ import { toAgUiFrame } from './frames.js';
40
3
  /**
41
- * Projects one library run's stream ({@link StreamFrame}s, in buffer order) onto AG-UI 1.0 events.
42
- *
43
- * A pure state machine: the same frames always give the same events, which is what lets a resuming
44
- * request — served by any replica — replay the run's buffer, skip what the interrupted AG-UI run
45
- * already delivered, and continue. It writes nothing itself; {@link agUiEvents} drives it and
46
- * decides when the run has stopped to ask.
47
- *
48
- * What maps where:
49
- * - `text` → `TEXT_MESSAGE_*` (one message per stretch of text);
50
- * - `reasoning` → a reasoning span with one reasoning message;
51
- * - `step-start`/`step-finish` → `STEP_STARTED`/`STEP_FINISHED`, the step's usage folded into the
52
- * run's `usage`;
53
- * - `tool-input-*` → `TOOL_CALL_START`/`ARGS`/`END`; `tool-output*` → `TOOL_CALL_RESULT`;
54
- * - an approval or a question set the run parks on → an `Interrupt` on the closing `RUN_FINISHED`;
55
- * - `cancelled` → the cancelled outcome; an error frame → `RUN_ERROR`;
56
- * - generative UI, title, queue and the native approval/elicitation frames → `CUSTOM` events named
57
- * in {@link AG_UI_CUSTOM}, for what AG-UI has no event for.
58
- *
59
- * Not mapped: sub-agent attribution (`subagentRunId`) — a delegated agent's frames are projected
60
- * flat, as the protocol allows of a producer that does not attribute.
4
+ * The shared AG-UI encoder (`@dudousxd/nestjs-agent-core/ag-ui`, one implementation for both
5
+ * servers), reading this package's {@link StreamFrame}s. A pure state machine: the same frames
6
+ * always give the same events.
61
7
  */
62
8
  export class AgUiEncoder {
63
- options;
64
- position = 0;
65
- started = false;
66
- closed = false;
67
- cancelled = false;
68
- crossed = false;
69
- messageSeq = 0;
70
- stepSeq = 0;
71
- openText;
72
- openReasoning;
73
- /** The last text message of the current step: a tool call attaches to it. */
74
- stepMessage;
75
- openSteps = [];
76
- calls = new Map();
77
- pending = new Map();
78
- usage = new Map();
9
+ shared;
79
10
  constructor(options) {
80
- this.options = options;
11
+ this.shared = new SharedEncoder(options);
81
12
  }
82
13
  /** How many frames of the library stream this encoder has consumed. */
83
14
  get consumed() {
84
- return this.position;
15
+ return this.shared.consumed;
85
16
  }
86
17
  /** The run is waiting on at least one approval or question set nobody has settled yet. */
87
18
  get waiting() {
88
- return this.pending.size > 0;
19
+ return this.shared.waiting;
89
20
  }
90
- /**
91
- * Is anything still in flight that is NOT waiting on a person — a tool call announced and neither
92
- * answered nor parked? While there is, more frames are coming without anyone's help.
93
- */
21
+ /** A tool call is announced and neither answered nor parked: more frames are coming. */
94
22
  get busy() {
95
- for (const [id, call] of this.calls) {
96
- if (!call.answered && !this.pending.has(id))
97
- return true;
98
- }
99
- return false;
23
+ return this.shared.busy;
100
24
  }
101
- /** `RUN_STARTED`, and the library's own ids for the run. Call once, before the first frame. */
102
25
  start() {
103
- if (this.started)
104
- return [];
105
- this.started = true;
106
- return [
107
- {
108
- type: 'RUN_STARTED',
109
- threadId: this.options.threadId,
110
- runId: this.options.runId,
111
- protocolVersion: AG_UI_PROTOCOL_VERSION,
112
- },
113
- {
114
- type: 'CUSTOM',
115
- name: AG_UI_CUSTOM.run,
116
- value: {
117
- runId: this.options.streamRunId,
118
- threadId: this.options.streamThreadId ?? this.options.threadId,
119
- },
120
- },
121
- ];
26
+ return this.shared.start();
122
27
  }
123
- /** The AG-UI events the next frame of the library stream stands for (possibly none). */
124
28
  encode(frame) {
125
- const position = this.position;
126
- this.position += 1;
127
- if (this.closed)
128
- return [];
129
- const skip = this.options.skip ?? 0;
130
- if (!this.crossed && position >= skip)
131
- this.crossBoundary();
132
- const events = this.project(frame, position);
133
- // A frame an earlier AG-UI run already delivered still moves the state (what is open, what is
134
- // pending; NOT what was spent — usage is per run) but writes nothing. The last of them is the
135
- // boundary: crossed at once, not when the next frame happens to arrive, because a run that was
136
- // just answered may take a moment to write one.
137
- if (position < skip) {
138
- if (this.position >= skip)
139
- this.crossBoundary();
140
- return [];
141
- }
142
- return events;
143
- }
144
- /**
145
- * The replayed frames are behind us: from here on events are written. The AG-UI run that delivered
146
- * them closed everything it had open before it finished, so this run starts with nothing open, and
147
- * what the resuming request answered is no longer waiting.
148
- */
149
- crossBoundary() {
150
- this.crossed = true;
151
- this.openText = undefined;
152
- this.openReasoning = undefined;
153
- this.stepMessage = undefined;
154
- this.openSteps.length = 0;
155
- for (const call of this.calls.values()) {
156
- call.started = false;
157
- call.ended = true;
158
- }
159
- for (const id of this.options.answered ?? [])
160
- this.pending.delete(id);
161
- }
162
- get replaying() {
163
- return this.position <= (this.options.skip ?? 0);
29
+ return this.shared.encode(toAgUiFrame(frame));
164
30
  }
165
- project(frame, position) {
166
- switch (frame.t) {
167
- case 'text':
168
- return frame.v.length > 0 ? this.textDelta(frame.v) : [];
169
- case 'component':
170
- return [
171
- this.custom(AG_UI_CUSTOM.ui, {
172
- id: frame.id ?? `ui:${position}`,
173
- component: frame.name,
174
- props: frame.data,
175
- ...(frame.version !== undefined ? { version: frame.version } : {}),
176
- ...(frame.toolCallId !== undefined ? { toolCallId: frame.toolCallId } : {}),
177
- }),
178
- ];
179
- case 'approval': {
180
- this.pending.set(frame.id, {
181
- kind: 'approval',
182
- parked: frame.runId,
183
- toolCallId: frame.id,
184
- interrupt: {
185
- reason: 'tool_approval',
186
- message: `Approve ${frame.toolName}?`,
187
- toolCallId: frame.id,
188
- responseSchema: APPROVAL_SCHEMA,
189
- ...(frame.expiresAt !== undefined ? { expiresAt: frame.expiresAt } : {}),
190
- metadata: {
191
- 'agora.toolName': frame.toolName,
192
- 'agora.input': frame.input ?? null,
193
- 'agora.approver': frame.approver ?? 'requester',
194
- },
195
- },
196
- });
197
- return [
198
- this.custom(AG_UI_CUSTOM.approvalRequested, {
199
- id: frame.id,
200
- runId: frame.runId,
201
- toolName: frame.toolName,
202
- input: frame.input ?? null,
203
- approver: frame.approver ?? 'requester',
204
- ...(frame.expiresAt !== undefined ? { expiresAt: frame.expiresAt } : {}),
205
- }),
206
- ];
207
- }
208
- case 'elicitation': {
209
- this.pending.set(frame.id, {
210
- kind: 'elicitation',
211
- parked: frame.runId,
212
- toolCallId: frame.id,
213
- interrupt: {
214
- reason: 'input_required',
215
- message: frame.request.preamble ?? frame.request.questions[0]?.prompt ?? 'Input required',
216
- // An intake's question set belongs to no tool call the stream announced.
217
- ...(this.calls.has(frame.id) ? { toolCallId: frame.id } : {}),
218
- responseSchema: elicitationSchema(frame.request),
219
- metadata: { 'agora.request': frame.request },
220
- },
221
- });
222
- return [
223
- this.custom(AG_UI_CUSTOM.elicitation, { runId: frame.runId, request: frame.request }),
224
- ];
225
- }
226
- case 'error': {
227
- this.closed = true;
228
- const usage = this.usageEntries();
229
- return [
230
- {
231
- type: 'RUN_ERROR',
232
- message: frame.message,
233
- code: frame.code,
234
- ...(usage.length > 0 ? { usage } : {}),
235
- },
236
- ];
237
- }
238
- case 'event':
239
- return this.event(frame.event);
240
- }
241
- }
242
- event(event) {
243
- switch (event.kind) {
244
- case 'text':
245
- return event.text.length > 0 ? this.textDelta(event.text) : [];
246
- case 'reasoning':
247
- return event.text.length > 0 ? this.reasoningDelta(event.text) : [];
248
- case 'step-start': {
249
- this.stepSeq += 1;
250
- const stepName = `step-${this.stepSeq}`;
251
- this.openSteps.push(stepName);
252
- this.stepMessage = undefined;
253
- return [...this.closeStreams(), { type: 'STEP_STARTED', stepName }];
254
- }
255
- case 'step-finish': {
256
- const out = this.closeStreams();
257
- if (!this.replaying)
258
- this.addUsage(event.usage, event.model);
259
- const stepName = this.openSteps.pop();
260
- if (stepName !== undefined)
261
- out.push({ type: 'STEP_FINISHED', stepName });
262
- if (event.usage !== undefined || event.costUsd !== undefined) {
263
- out.push(this.custom(AG_UI_CUSTOM.stepUsage, {
264
- ...(event.usage !== undefined ? { usage: event.usage } : {}),
265
- ...(event.costUsd !== undefined ? { costUsd: event.costUsd } : {}),
266
- ...(event.reasoningMs !== undefined ? { reasoningMs: event.reasoningMs } : {}),
267
- }));
268
- }
269
- return out;
270
- }
271
- case 'tool-input-start': {
272
- if (this.calls.has(event.id))
273
- return [];
274
- const out = this.closeStreams();
275
- this.calls.set(event.id, {
276
- name: event.name,
277
- started: true,
278
- ended: false,
279
- sawDelta: false,
280
- answered: false,
281
- });
282
- out.push(this.callStart(event.id, event.name));
283
- return out;
284
- }
285
- case 'tool-input-delta': {
286
- const call = this.calls.get(event.id);
287
- if (call === undefined || call.ended || event.delta.length === 0)
288
- return [];
289
- call.sawDelta = true;
290
- return [{ type: 'TOOL_CALL_ARGS', toolCallId: event.id, delta: event.delta }];
291
- }
292
- case 'tool-input-available': {
293
- const known = this.calls.get(event.id);
294
- if (known?.ended === true)
295
- return [];
296
- const out = this.closeStreams();
297
- const call = known ?? {
298
- name: event.name,
299
- started: false,
300
- ended: false,
301
- sawDelta: false,
302
- answered: false,
303
- };
304
- if (!call.started) {
305
- call.started = true;
306
- out.push(this.callStart(event.id, event.name));
307
- }
308
- if (!call.sawDelta) {
309
- out.push({
310
- type: 'TOOL_CALL_ARGS',
311
- toolCallId: event.id,
312
- delta: text(event.input ?? {}),
313
- });
314
- }
315
- call.ended = true;
316
- this.calls.set(event.id, call);
317
- out.push({ type: 'TOOL_CALL_END', toolCallId: event.id });
318
- return out;
319
- }
320
- case 'tool-output':
321
- return this.result(event.id, text(event.output));
322
- case 'tool-output-error':
323
- return this.result(event.id, event.error, { 'agora.outcome': 'error' });
324
- case 'tool-output-denied':
325
- return this.result(event.id, event.reason ?? 'denied', { 'agora.outcome': 'denied' });
326
- case 'approval-requested':
327
- // Written by the loop as a typed `approval` frame; an event of this kind reaching the sink
328
- // directly is carried as-is rather than guessed into an interrupt without its run id.
329
- return [this.custom(AG_UI_CUSTOM.approvalRequested, event)];
330
- case 'approval-settled':
331
- this.pending.delete(event.id);
332
- return [this.custom(AG_UI_CUSTOM.approvalSettled, event)];
333
- case 'elicitation':
334
- return [this.custom(AG_UI_CUSTOM.elicitation, { request: event.request })];
335
- case 'ui': {
336
- const { kind: _kind, ...component } = event;
337
- return [this.custom(AG_UI_CUSTOM.ui, component)];
338
- }
339
- case 'title':
340
- return [this.custom(AG_UI_CUSTOM.title, { title: event.title })];
341
- case 'queue':
342
- return [
343
- this.custom(AG_UI_CUSTOM.queue, {
344
- queue: event.queue,
345
- ...(event.started !== undefined ? { started: event.started } : {}),
346
- }),
347
- ];
348
- case 'cancelled':
349
- this.cancelled = true;
350
- return [];
351
- default:
352
- // A kind newer than this encoder: tolerated, as every reader of the stream must.
353
- return [];
354
- }
355
- }
356
- /**
357
- * The events that end the AG-UI run: whatever is still open is closed first, then `RUN_FINISHED`
358
- * with how it ended — interrupted when the run is waiting on someone, cancelled when it was
359
- * stopped, success otherwise. Nothing after a `RUN_ERROR`. `ended` says the library run's stream
360
- * itself ended, rather than this reader having stopped following it.
361
- */
362
31
  finish(ended = false) {
363
- if (this.closed)
364
- return [];
365
- this.closed = true;
366
- if (!this.crossed)
367
- this.crossBoundary();
368
- // A library run that ended is waiting on no one, whatever it asked along the way.
369
- if (ended)
370
- this.pending.clear();
371
- const out = this.start();
372
- out.push(...this.closeStreams());
373
- for (const [id, call] of this.calls) {
374
- if (call.started && !call.ended) {
375
- call.ended = true;
376
- out.push({ type: 'TOOL_CALL_END', toolCallId: id });
377
- }
378
- }
379
- while (this.openSteps.length > 0) {
380
- out.push({ type: 'STEP_FINISHED', stepName: this.openSteps.pop() });
381
- }
382
- const usage = this.usageEntries();
383
- const interrupts = this.cancelled ? [] : this.interrupts();
384
- out.push({
385
- type: 'RUN_FINISHED',
386
- threadId: this.options.threadId,
387
- runId: this.options.runId,
388
- ...(this.cancelled
389
- ? { outcome: { type: 'cancelled' } }
390
- : interrupts.length > 0
391
- ? { outcome: { type: 'interrupt', interrupts } }
392
- : {}),
393
- ...(usage.length > 0 ? { usage } : {}),
394
- });
395
- return out;
396
- }
397
- interrupts() {
398
- return [...this.pending.values()].map((entry) => ({
399
- id: encodeInterruptId({
400
- kind: entry.kind,
401
- parked: entry.parked,
402
- stream: this.options.streamRunId,
403
- toolCallId: entry.toolCallId,
404
- position: this.position,
405
- }),
406
- ...entry.interrupt,
407
- }));
408
- }
409
- custom(name, value) {
410
- return { type: 'CUSTOM', name, value };
411
- }
412
- nextMessageId() {
413
- this.messageSeq += 1;
414
- return `${this.options.runId}:m${this.messageSeq}`;
415
- }
416
- textDelta(delta) {
417
- const out = [];
418
- if (this.openReasoning !== undefined)
419
- out.push(...this.closeReasoning());
420
- if (this.openText === undefined) {
421
- this.openText = this.nextMessageId();
422
- this.stepMessage = this.openText;
423
- out.push({ type: 'TEXT_MESSAGE_START', messageId: this.openText, role: 'assistant' });
424
- }
425
- out.push({ type: 'TEXT_MESSAGE_CONTENT', messageId: this.openText, delta });
426
- return out;
427
- }
428
- reasoningDelta(delta) {
429
- const out = [];
430
- if (this.openText !== undefined)
431
- out.push(...this.closeText());
432
- if (this.openReasoning === undefined) {
433
- this.openReasoning = this.nextMessageId();
434
- out.push({ type: 'REASONING_START', messageId: this.openReasoning }, { type: 'REASONING_MESSAGE_START', messageId: this.openReasoning, role: 'reasoning' });
435
- }
436
- out.push({ type: 'REASONING_MESSAGE_CONTENT', messageId: this.openReasoning, delta });
437
- return out;
438
- }
439
- closeText() {
440
- if (this.openText === undefined)
441
- return [];
442
- const messageId = this.openText;
443
- this.openText = undefined;
444
- return [{ type: 'TEXT_MESSAGE_END', messageId }];
445
- }
446
- closeReasoning() {
447
- if (this.openReasoning === undefined)
448
- return [];
449
- const messageId = this.openReasoning;
450
- this.openReasoning = undefined;
451
- return [
452
- { type: 'REASONING_MESSAGE_END', messageId },
453
- { type: 'REASONING_END', messageId },
454
- ];
455
- }
456
- closeStreams() {
457
- return [...this.closeReasoning(), ...this.closeText()];
458
- }
459
- callStart(id, name) {
460
- return {
461
- type: 'TOOL_CALL_START',
462
- toolCallId: id,
463
- toolCallName: name,
464
- ...(this.stepMessage !== undefined ? { parentMessageId: this.stepMessage } : {}),
465
- };
466
- }
467
- result(id, content, metadata) {
468
- const call = this.calls.get(id);
469
- if (call !== undefined)
470
- call.answered = true;
471
- else {
472
- this.calls.set(id, {
473
- name: '',
474
- started: false,
475
- ended: true,
476
- sawDelta: false,
477
- answered: true,
478
- });
479
- }
480
- this.pending.delete(id);
481
- return [
482
- ...this.closeStreams(),
483
- {
484
- type: 'TOOL_CALL_RESULT',
485
- messageId: `${this.options.runId}:tool:${id}`,
486
- toolCallId: id,
487
- content,
488
- role: 'tool',
489
- ...(metadata !== undefined ? { metadata } : {}),
490
- },
491
- ];
492
- }
493
- addUsage(usage, model) {
494
- if (usage === undefined)
495
- return;
496
- const name = typeof model === 'string' && model.length > 0 ? model : this.options.model;
497
- const key = name ?? '';
498
- const entry = this.usage.get(key) ?? (name !== undefined ? { model: name } : {});
499
- // The library's accounting is already the protocol's: input and output are totals, cache and
500
- // reasoning counts are parts of them. A count the provider did not report stays absent.
501
- entry.inputTokens = (entry.inputTokens ?? 0) + usage.inputTokens;
502
- entry.outputTokens = (entry.outputTokens ?? 0) + usage.outputTokens;
503
- entry.totalTokens = entry.inputTokens + entry.outputTokens;
504
- if (usage.cacheReadTokens !== undefined) {
505
- entry.cachedInputTokens = (entry.cachedInputTokens ?? 0) + usage.cacheReadTokens;
506
- }
507
- if (usage.cacheWriteTokens !== undefined) {
508
- entry.cacheWriteInputTokens = (entry.cacheWriteInputTokens ?? 0) + usage.cacheWriteTokens;
509
- }
510
- if (usage.reasoningTokens !== undefined) {
511
- entry.reasoningTokens = (entry.reasoningTokens ?? 0) + usage.reasoningTokens;
512
- }
513
- this.usage.set(key, entry);
514
- }
515
- usageEntries() {
516
- return [...this.usage.values()];
32
+ return this.shared.finish(ended);
517
33
  }
518
34
  }
519
35
  //# sourceMappingURL=encoder.js.map