@nxisai/agent-harness 0.2.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 (126) hide show
  1. package/README.md +434 -0
  2. package/dist/a2ui/cards.d.ts +55 -0
  3. package/dist/a2ui/cards.d.ts.map +1 -0
  4. package/dist/a2ui/cards.js +133 -0
  5. package/dist/a2ui/cards.js.map +1 -0
  6. package/dist/a2ui/emit.d.ts +33 -0
  7. package/dist/a2ui/emit.d.ts.map +1 -0
  8. package/dist/a2ui/emit.js +116 -0
  9. package/dist/a2ui/emit.js.map +1 -0
  10. package/dist/a2ui/form.d.ts +20 -0
  11. package/dist/a2ui/form.d.ts.map +1 -0
  12. package/dist/a2ui/form.js +92 -0
  13. package/dist/a2ui/form.js.map +1 -0
  14. package/dist/a2ui/host.d.ts +34 -0
  15. package/dist/a2ui/host.d.ts.map +1 -0
  16. package/dist/a2ui/host.js +47 -0
  17. package/dist/a2ui/host.js.map +1 -0
  18. package/dist/a2ui/index.d.ts +23 -0
  19. package/dist/a2ui/index.d.ts.map +1 -0
  20. package/dist/a2ui/index.js +20 -0
  21. package/dist/a2ui/index.js.map +1 -0
  22. package/dist/a2ui/stepper.d.ts +26 -0
  23. package/dist/a2ui/stepper.d.ts.map +1 -0
  24. package/dist/a2ui/stepper.js +162 -0
  25. package/dist/a2ui/stepper.js.map +1 -0
  26. package/dist/a2ui/theme.d.ts +21 -0
  27. package/dist/a2ui/theme.d.ts.map +1 -0
  28. package/dist/a2ui/theme.js +134 -0
  29. package/dist/a2ui/theme.js.map +1 -0
  30. package/dist/a2ui/to-v09.d.ts +31 -0
  31. package/dist/a2ui/to-v09.d.ts.map +1 -0
  32. package/dist/a2ui/to-v09.js +104 -0
  33. package/dist/a2ui/to-v09.js.map +1 -0
  34. package/dist/engine.d.ts +209 -0
  35. package/dist/engine.d.ts.map +1 -0
  36. package/dist/engine.js +505 -0
  37. package/dist/engine.js.map +1 -0
  38. package/dist/harness/harness.d.ts +284 -0
  39. package/dist/harness/harness.d.ts.map +1 -0
  40. package/dist/harness/harness.js +568 -0
  41. package/dist/harness/harness.js.map +1 -0
  42. package/dist/harness/plan.d.ts +32 -0
  43. package/dist/harness/plan.d.ts.map +1 -0
  44. package/dist/harness/plan.js +68 -0
  45. package/dist/harness/plan.js.map +1 -0
  46. package/dist/harness/policy.d.ts +49 -0
  47. package/dist/harness/policy.d.ts.map +1 -0
  48. package/dist/harness/policy.js +34 -0
  49. package/dist/harness/policy.js.map +1 -0
  50. package/dist/harness/renderer.d.ts +163 -0
  51. package/dist/harness/renderer.d.ts.map +1 -0
  52. package/dist/harness/renderer.js +12 -0
  53. package/dist/harness/renderer.js.map +1 -0
  54. package/dist/harness/router/coordinator.d.ts +120 -0
  55. package/dist/harness/router/coordinator.d.ts.map +1 -0
  56. package/dist/harness/router/coordinator.js +145 -0
  57. package/dist/harness/router/coordinator.js.map +1 -0
  58. package/dist/harness/router/rules.d.ts +48 -0
  59. package/dist/harness/router/rules.d.ts.map +1 -0
  60. package/dist/harness/router/rules.js +38 -0
  61. package/dist/harness/router/rules.js.map +1 -0
  62. package/dist/harness/slots.d.ts +73 -0
  63. package/dist/harness/slots.d.ts.map +1 -0
  64. package/dist/harness/slots.js +68 -0
  65. package/dist/harness/slots.js.map +1 -0
  66. package/dist/harness/tools/bridge.d.ts +71 -0
  67. package/dist/harness/tools/bridge.d.ts.map +1 -0
  68. package/dist/harness/tools/bridge.js +271 -0
  69. package/dist/harness/tools/bridge.js.map +1 -0
  70. package/dist/harness/tools/client.d.ts +11 -0
  71. package/dist/harness/tools/client.d.ts.map +1 -0
  72. package/dist/harness/tools/client.js +21 -0
  73. package/dist/harness/tools/client.js.map +1 -0
  74. package/dist/harness/tools/discovery.d.ts +13 -0
  75. package/dist/harness/tools/discovery.d.ts.map +1 -0
  76. package/dist/harness/tools/discovery.js +43 -0
  77. package/dist/harness/tools/discovery.js.map +1 -0
  78. package/dist/harness/tools/local-context.d.ts +38 -0
  79. package/dist/harness/tools/local-context.d.ts.map +1 -0
  80. package/dist/harness/tools/local-context.js +55 -0
  81. package/dist/harness/tools/local-context.js.map +1 -0
  82. package/dist/harness/tools/schema.d.ts +52 -0
  83. package/dist/harness/tools/schema.d.ts.map +1 -0
  84. package/dist/harness/tools/schema.js +175 -0
  85. package/dist/harness/tools/schema.js.map +1 -0
  86. package/dist/harness/tools/source.d.ts +65 -0
  87. package/dist/harness/tools/source.d.ts.map +1 -0
  88. package/dist/harness/tools/source.js +89 -0
  89. package/dist/harness/tools/source.js.map +1 -0
  90. package/dist/harness/tools/webmcp.d.ts +50 -0
  91. package/dist/harness/tools/webmcp.d.ts.map +1 -0
  92. package/dist/harness/tools/webmcp.js +114 -0
  93. package/dist/harness/tools/webmcp.js.map +1 -0
  94. package/dist/index.d.ts +49 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +38 -0
  97. package/dist/index.js.map +1 -0
  98. package/dist/providers/types.d.ts +123 -0
  99. package/dist/providers/types.d.ts.map +1 -0
  100. package/dist/providers/types.js +2 -0
  101. package/dist/providers/types.js.map +1 -0
  102. package/dist/types.d.ts +223 -0
  103. package/dist/types.d.ts.map +1 -0
  104. package/dist/types.js +8 -0
  105. package/dist/types.js.map +1 -0
  106. package/dist/ui/agent-fullscreen.d.ts +41 -0
  107. package/dist/ui/agent-fullscreen.d.ts.map +1 -0
  108. package/dist/ui/agent-fullscreen.js +2821 -0
  109. package/dist/ui/agent-fullscreen.js.map +1 -0
  110. package/dist/ui/config.d.ts +88 -0
  111. package/dist/ui/config.d.ts.map +1 -0
  112. package/dist/ui/config.js +96 -0
  113. package/dist/ui/config.js.map +1 -0
  114. package/dist/ui/index.d.ts +22 -0
  115. package/dist/ui/index.d.ts.map +1 -0
  116. package/dist/ui/index.js +19 -0
  117. package/dist/ui/index.js.map +1 -0
  118. package/dist/ui/tailwind.generated.d.ts +2 -0
  119. package/dist/ui/tailwind.generated.d.ts.map +1 -0
  120. package/dist/ui/tailwind.generated.js +3 -0
  121. package/dist/ui/tailwind.generated.js.map +1 -0
  122. package/dist/webmcp-contract.d.ts +57 -0
  123. package/dist/webmcp-contract.d.ts.map +1 -0
  124. package/dist/webmcp-contract.js +11 -0
  125. package/dist/webmcp-contract.js.map +1 -0
  126. package/package.json +39 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Arranges missing values into the steps a renderer asks for them in.
3
+ *
4
+ * A single missing value is one question; several become a stepper.
5
+ *
6
+ * @module
7
+ */
8
+ /**
9
+ * Field names conventionally asked together, so they share a step.
10
+ *
11
+ * Matched by exact field name. Kept short by design: the more fields are
12
+ * grouped, the less the step count communicates.
13
+ */
14
+ const AFFINITY = [
15
+ ['full_name', 'name', 'email', 'phone'],
16
+ ['city', 'state', 'zip', 'postal_code'],
17
+ ];
18
+ /** Index of the {@link AFFINITY} group a field belongs to, or -1. */
19
+ function affinityGroup(name) {
20
+ return AFFINITY.findIndex(group => group.includes(name));
21
+ }
22
+ /**
23
+ * Arrange missing slots into steps.
24
+ *
25
+ * @param missing - The slots still needing values, in the order to ask for them.
26
+ * @param groupRelated - Whether to place related fields (see {@link AFFINITY})
27
+ * on one step. Pass `false` to ask for every field separately.
28
+ * @returns A plan whose `stepped` flag is true when it has more than one step.
29
+ */
30
+ export function buildPlan(missing, groupRelated = true) {
31
+ if (!missing.length)
32
+ return { steps: [], stepped: false };
33
+ const steps = [];
34
+ if (groupRelated) {
35
+ const used = new Set();
36
+ for (const slot of missing) {
37
+ if (used.has(slot))
38
+ continue;
39
+ const group = affinityGroup(slot.name);
40
+ const together = group === -1
41
+ ? [slot]
42
+ : missing.filter(s => !used.has(s) && affinityGroup(s.name) === group);
43
+ together.forEach(s => used.add(s));
44
+ steps.push({ index: steps.length, slots: together, title: stepTitle(together) });
45
+ }
46
+ }
47
+ else {
48
+ for (const slot of missing) {
49
+ steps.push({ index: steps.length, slots: [slot], title: stepTitle([slot]) });
50
+ }
51
+ }
52
+ // Renumber after grouping so index always matches position.
53
+ steps.forEach((s, i) => (s.index = i));
54
+ return { steps, stepped: steps.length > 1 };
55
+ }
56
+ /** A human label for a slot: its description, or its name made readable. */
57
+ function label(slot) {
58
+ return (slot.schema.description ||
59
+ slot.name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ')
60
+ .replace(/^\w/, c => c.toUpperCase()));
61
+ }
62
+ /** The heading for a step, joining grouped slots with an ampersand. */
63
+ function stepTitle(slots) {
64
+ if (slots.length === 1)
65
+ return label(slots[0]);
66
+ return slots.map(label).join(' & ');
67
+ }
68
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/harness/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Tool policy: whether a resolved tool call runs immediately or waits for a
3
+ * human confirmation.
4
+ *
5
+ * The decision comes from the tool's own `readOnlyHint` annotation, so a tool
6
+ * source that annotates its tools correctly gets the right behaviour with no
7
+ * further configuration.
8
+ *
9
+ * @module
10
+ */
11
+ import type { ModelContextToolInfo } from '../types.js';
12
+ /** What the harness should do once arguments are resolved. */
13
+ export type Decision =
14
+ /** Run it now, with no interstitial UI. */
15
+ 'execute'
16
+ /** Ask for the missing values, then run without a further confirmation. */
17
+ | 'ask'
18
+ /** Nothing is missing, but a human still presses Submit. */
19
+ | 'confirm';
20
+ /** How a single tool should be treated once its arguments are known. */
21
+ export interface ToolPolicy {
22
+ /** Taken from `annotations.readOnlyHint`. Read-only tools never write. */
23
+ readOnly: boolean;
24
+ /**
25
+ * Whether a call with no missing values still needs a human press.
26
+ *
27
+ * True for writes, whose effects reach a real system and cannot be undone by
28
+ * the harness. The confirmation displays the values read-only: nothing is
29
+ * missing, so there is nothing to edit.
30
+ */
31
+ confirmWhenSatisfied: boolean;
32
+ }
33
+ /**
34
+ * Derive a tool's policy from its annotations.
35
+ *
36
+ * @param tool - The tool as reported by the tool source.
37
+ * @returns The policy the harness applies to calls of this tool.
38
+ */
39
+ export declare function policyFor(tool: ModelContextToolInfo): ToolPolicy;
40
+ /**
41
+ * Resolve the decision from a tool's policy and what is still unknown.
42
+ *
43
+ * @param policy - The policy for the tool being called.
44
+ * @param gapCount - How many required values are still missing.
45
+ * @returns `'ask'` when anything is missing, otherwise `'confirm'` for writes
46
+ * and `'execute'` for reads.
47
+ */
48
+ export declare function decide(policy: ToolPolicy, gapCount: number): Decision;
49
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/harness/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,8DAA8D;AAC9D,MAAM,MAAM,QAAQ;AAClB,2CAA2C;AACzC,SAAS;AACX,2EAA2E;GACzE,KAAK;AACP,4DAA4D;GAC1D,SAAS,CAAC;AAEd,wEAAwE;AACxE,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,oBAAoB,GAAG,UAAU,CAGhE;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAGrE"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tool policy: whether a resolved tool call runs immediately or waits for a
3
+ * human confirmation.
4
+ *
5
+ * The decision comes from the tool's own `readOnlyHint` annotation, so a tool
6
+ * source that annotates its tools correctly gets the right behaviour with no
7
+ * further configuration.
8
+ *
9
+ * @module
10
+ */
11
+ /**
12
+ * Derive a tool's policy from its annotations.
13
+ *
14
+ * @param tool - The tool as reported by the tool source.
15
+ * @returns The policy the harness applies to calls of this tool.
16
+ */
17
+ export function policyFor(tool) {
18
+ const readOnly = !!tool.annotations?.readOnlyHint;
19
+ return { readOnly, confirmWhenSatisfied: !readOnly };
20
+ }
21
+ /**
22
+ * Resolve the decision from a tool's policy and what is still unknown.
23
+ *
24
+ * @param policy - The policy for the tool being called.
25
+ * @param gapCount - How many required values are still missing.
26
+ * @returns `'ask'` when anything is missing, otherwise `'confirm'` for writes
27
+ * and `'execute'` for reads.
28
+ */
29
+ export function decide(policy, gapCount) {
30
+ if (gapCount > 0)
31
+ return 'ask';
32
+ return policy.confirmWhenSatisfied ? 'confirm' : 'execute';
33
+ }
34
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/harness/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA2BH;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,IAA0B;IAClD,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;IAClD,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC,QAAQ,EAAE,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,MAAkB,EAAE,QAAgB;IACzD,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,OAAO,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * The interface between the harness and a user interface.
3
+ *
4
+ * The harness makes every decision about what to do; a renderer is asked only
5
+ * to draw and to report what a person did. Keeping this interface narrow is
6
+ * what allows the same harness to drive the bundled web component, an A2UI
7
+ * surface, or a UI of your own.
8
+ *
9
+ * @module
10
+ */
11
+ import type { ModelContextToolInfo, ModelContextToolResponse } from '../types.js';
12
+ import type { ClarificationPlan } from './plan.js';
13
+ import type { Resolution, Slot } from './slots.js';
14
+ /** One tool call and its response, for renderers that show their work. */
15
+ export interface TraceEntry {
16
+ /** The tool that was called. */
17
+ tool: ModelContextToolInfo;
18
+ /** Arguments as sent, after the harness reconciled and filled them. */
19
+ args: Record<string, unknown>;
20
+ /** The response, flattened to text. */
21
+ text: string;
22
+ /** Whether the tool reported a failure. */
23
+ isError: boolean;
24
+ /** How many records came back, when the response was a record set. */
25
+ count?: number;
26
+ }
27
+ /**
28
+ * The assistant's bubble for one phase of a turn.
29
+ *
30
+ * A turn is not one continuous stream. The harness opens a bubble, moves it
31
+ * through its phases and finalizes it, then opens another when the next silence
32
+ * begins, so {@link HarnessRenderer.streaming} is called more than once per turn
33
+ * and each bubble should be treated as independent.
34
+ *
35
+ * The usual order:
36
+ *
37
+ * ```ts
38
+ * bubble.thinking('Loading model…'); // no output yet
39
+ * bubble.update('The nearest plumb'); // text has started arriving
40
+ * bubble.finalize('The nearest plumber is…'); // the phase is over
41
+ * ```
42
+ */
43
+ export interface StreamingBubble {
44
+ /**
45
+ * Show that work is happening, before there is anything to show.
46
+ *
47
+ * A turn can be silent for a long time: loading model weights takes tens of
48
+ * seconds on a first visit. Safe to call repeatedly, since each call replaces
49
+ * the label in place rather than adding a bubble.
50
+ *
51
+ * @param label - The current phase, such as `'Loading model…'`. Omit it to
52
+ * indicate work with nothing worth naming.
53
+ */
54
+ thinking(label?: string): void;
55
+ /**
56
+ * Stream the answer as it is produced.
57
+ *
58
+ * @param text - The whole visible answer so far, not the newest fragment.
59
+ * Replace the bubble's contents with it; appending accumulates the answer on
60
+ * itself. Tool-call markers are already stripped.
61
+ */
62
+ update(text: string): void;
63
+ /**
64
+ * Close the bubble with its final text.
65
+ *
66
+ * Usually the last call a bubble receives, but not guaranteed to be: when the
67
+ * model answers with a question instead of calling a tool, the harness runs a
68
+ * second generation into the same bubble, so `update` can follow a non-empty
69
+ * `finalize`. Implementations should stay usable rather than throwing.
70
+ *
71
+ * @param text - The text to keep. Empty means the phase produced nothing
72
+ * worth keeping (a failed load, a finished tool call, a generation that was
73
+ * only a tool call), and the bubble should be removed rather than left blank.
74
+ */
75
+ finalize(text: string): void;
76
+ }
77
+ /** A write tool with every value resolved, awaiting a human press. */
78
+ export interface ConfirmRequest {
79
+ /** The tool about to be called. */
80
+ tool: ModelContextToolInfo;
81
+ /** Rows to display read-only. Nothing is missing, so nothing is editable. */
82
+ slots: Slot[];
83
+ /** The values that will be sent. */
84
+ values: Record<string, unknown>;
85
+ /** Call to proceed, optionally with values the user amended. */
86
+ onSubmit(values: Record<string, unknown>): void;
87
+ /** Call if the user declines. */
88
+ onCancel(): void;
89
+ /** Call to open the full editable form instead of confirming. */
90
+ onEdit(): void;
91
+ }
92
+ /** A tool call missing required values, and the plan for asking about them. */
93
+ export interface AskRequest {
94
+ /** The tool waiting on the answers. */
95
+ tool: ModelContextToolInfo;
96
+ /** The questions to ask, as one step or several. */
97
+ plan: ClarificationPlan;
98
+ /** What is already known, and from where. */
99
+ resolution: Resolution;
100
+ /** Call with the merged values once the last step is answered. */
101
+ onComplete(values: Record<string, unknown>): void;
102
+ /** Call if the user abandons the questions. */
103
+ onCancel(): void;
104
+ }
105
+ /**
106
+ * Everything the harness needs from a user interface.
107
+ *
108
+ * Required members are called during an ordinary turn. Optional ones may be
109
+ * omitted by a renderer that has nothing to show for them.
110
+ */
111
+ export interface HarnessRenderer {
112
+ /** Add a complete message, already known in full. */
113
+ message(role: 'user' | 'assistant', text: string): void;
114
+ /**
115
+ * Open a bubble for the phase about to start.
116
+ *
117
+ * Called several times per turn: once before the model is touched, so the
118
+ * thread is never silent while weights download, and again around each tool
119
+ * call. Each bubble is finalized before the next is requested.
120
+ *
121
+ * @returns The bubble the harness will drive through that phase.
122
+ */
123
+ streaming(): StreamingBubble;
124
+ /**
125
+ * Show a one-line status outside the thread, such as a header strip.
126
+ *
127
+ * @param text - The status. Empty clears it.
128
+ * @param isError - Whether to present the status as a failure.
129
+ */
130
+ status(text: string, isError?: boolean): void;
131
+ /** Ask for missing values, as one question or a stepper. */
132
+ ask(req: AskRequest): void;
133
+ /** Present a write for confirmation. Nothing is missing. */
134
+ confirm(req: ConfirmRequest): void;
135
+ /** A read tool returned records. */
136
+ results(tool: ModelContextToolInfo, res: ModelContextToolResponse): void;
137
+ /** A write tool succeeded. */
138
+ detail(tool: ModelContextToolInfo, values: Record<string, unknown>, text: string): void;
139
+ /** A tool failed, or returned something that cannot be rendered. */
140
+ toolError(tool: ModelContextToolInfo, message: string): void;
141
+ /**
142
+ * Record the exchange behind a rendered card.
143
+ *
144
+ * Distinct from {@link HarnessRenderer.results} and
145
+ * {@link HarnessRenderer.detail}, which draw the outcome the user asked for.
146
+ * A trace is the underlying call and raw response, suited to a collapsed
147
+ * panel. Omit it to record nothing.
148
+ */
149
+ trace?(entry: TraceEntry): void;
150
+ /** Tear down any live ask or confirm UI. */
151
+ dismissPrompt?(): void;
152
+ /**
153
+ * Report whether the turn can still be stopped.
154
+ *
155
+ * Called with `true` when a turn starts and `false` when it ends, and also
156
+ * `false` as soon as a tool is invoked: past that point the call may already
157
+ * have reached a server, so it can no longer be cancelled.
158
+ *
159
+ * @param canStop - Whether {@link AgentHarness.stop} would still take effect.
160
+ */
161
+ stoppable?(canStop: boolean): void;
162
+ }
163
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/harness/renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The interface between the harness and a user interface.
3
+ *
4
+ * The harness makes every decision about what to do; a renderer is asked only
5
+ * to draw and to report what a person did. Keeping this interface narrow is
6
+ * what allows the same harness to drive the bundled web component, an A2UI
7
+ * surface, or a UI of your own.
8
+ *
9
+ * @module
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/harness/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Routes each turn to one of several {@link AgentHarness} instances.
3
+ *
4
+ * The coordinator holds no tools and produces no answers of its own. Its only
5
+ * job is choosing which agent handles a turn, so that agent sends the model its
6
+ * own few tool definitions rather than every tool on the page.
7
+ *
8
+ * It decides in four stages, cheapest first:
9
+ *
10
+ * | Stage | Cost | When it applies |
11
+ * |---|---|---|
12
+ * | `rules` | microseconds | A rule table matches the goal unambiguously. |
13
+ * | `sticky` | none | The rules abstained, and this conversation is already with an agent. |
14
+ * | `model` | one generation | The rules abstained and there is no history. Requires `provider`. |
15
+ * | `fallback` | none | Nothing else decided. |
16
+ *
17
+ * After the fact it applies one correction: if the chosen agent reports that
18
+ * the model asked for a tool it does not hold, the turn moves once to the agent
19
+ * that does.
20
+ *
21
+ * @module
22
+ */
23
+ import type { ChatMessage } from '../../types.js';
24
+ import type { ModelProvider } from '../../providers/types.js';
25
+ import type { AgentHarness, TurnResult } from '../harness.js';
26
+ import { type RouteDecision, type RouteRule } from './rules.js';
27
+ /** How a turn's agent was chosen. */
28
+ /** Which stage of {@link AgentCoordinator} chose the agent for a turn. */
29
+ export type RouteSource = 'rules' | 'sticky' | 'model' | 'fallback' | 'handback';
30
+ /** Configuration for an {@link AgentCoordinator}. */
31
+ export interface CoordinatorOptions {
32
+ /** The agents to route between, keyed by the name the rules refer to. */
33
+ agents: Record<string, AgentHarness>;
34
+ /** The rule table scored against each goal. May be empty. */
35
+ rules: RouteRule[];
36
+ /**
37
+ * The agent an otherwise unroutable turn goes to.
38
+ *
39
+ * Must name a key of {@link CoordinatorOptions.agents}; the constructor
40
+ * throws if it does not.
41
+ */
42
+ fallback: string;
43
+ /**
44
+ * Enables the model classification stage, which is otherwise skipped.
45
+ *
46
+ * This is the only part of routing that costs a generation, so it runs only
47
+ * when the rules abstain and no stickiness applies.
48
+ */
49
+ provider?: ModelProvider;
50
+ /** One line per agent describing what it handles, shown to the classifier. */
51
+ descriptions?: Record<string, string>;
52
+ /** Called with every routing decision, including corrections. */
53
+ onRoute?(decision: RoutedTurn): void;
54
+ }
55
+ /** A routing decision, reported through {@link CoordinatorOptions.onRoute}. */
56
+ export interface RoutedTurn extends RouteDecision {
57
+ /** The agent that ran, which may differ from {@link RouteDecision.route}. */
58
+ agent: string;
59
+ /** Which stage chose the agent. */
60
+ via: RouteSource;
61
+ /** True when no stage decided and the fallback was used. */
62
+ fellBack: boolean;
63
+ /** The user message that was routed. */
64
+ goal: string;
65
+ }
66
+ /**
67
+ * Routes turns between several agents. See the module description for the
68
+ * stages it decides in.
69
+ */
70
+ export declare class AgentCoordinator {
71
+ private readonly opts;
72
+ /**
73
+ * The agent each conversation is currently with.
74
+ *
75
+ * Keyed by the transcript array itself, so several conversations can be open
76
+ * at once and a discarded one takes its routing state with it.
77
+ */
78
+ private readonly sticky;
79
+ /** The classification pass in flight, if any. */
80
+ private classifying;
81
+ /** The agent handling the current turn, so {@link stop} can reach it. */
82
+ private working;
83
+ constructor(opts: CoordinatorOptions);
84
+ /**
85
+ * Abandon the turn in flight, whichever stage it has reached.
86
+ *
87
+ * Aborts the classification pass and delegates to the agent handling the
88
+ * turn, which resolves as `'stopped'`. Safe to call when nothing is running.
89
+ */
90
+ stop(): void;
91
+ /**
92
+ * Decide which agent should take a turn, without running it.
93
+ *
94
+ * Covers the rules, sticky and fallback stages. The model stage is skipped,
95
+ * since it is asynchronous and costs a generation.
96
+ *
97
+ * @param goal - The user's message.
98
+ * @param messages - The conversation, consulted for stickiness. Optional.
99
+ * @returns The chosen agent and the stage that chose it.
100
+ */
101
+ route(goal: string, messages?: ChatMessage[]): RoutedTurn;
102
+ /**
103
+ * Route a turn, run it, and re-route it once if it reached the wrong agent.
104
+ *
105
+ * @param goal - The user's message.
106
+ * @param messages - The live conversation, appended to in place.
107
+ * @returns The chosen agent's {@link TurnResult}.
108
+ */
109
+ turn(goal: string, messages: ChatMessage[]): Promise<TurnResult>;
110
+ /** The agent holding a tool, ignoring the one that handed the turn back. */
111
+ private ownerOf;
112
+ /**
113
+ * Ask the model which agent should take the turn.
114
+ *
115
+ * One short generation with no tools. Any reply that does not name a known
116
+ * agent returns null, leaving the fallback in place.
117
+ */
118
+ private classify;
119
+ }
120
+ //# sourceMappingURL=coordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator.d.ts","sourceRoot":"","sources":["../../../src/harness/router/coordinator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAc,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5E,qCAAqC;AACrC,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;AAEjF,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IACjC,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,6DAA6D;IAC7D,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,iEAAiE;IACjE,OAAO,CAAC,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;CACtC;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,GAAG,EAAE,WAAW,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;CACd;AAKD;;;GAGG;AACH,qBAAa,gBAAgB;IAaf,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,iDAAiD;IACjD,OAAO,CAAC,WAAW,CAAgC;IACnD,yEAAyE;IACzE,OAAO,CAAC,OAAO,CAA6B;gBAEf,IAAI,EAAE,kBAAkB;IAMrD;;;;;OAKG;IACH,IAAI,IAAI,IAAI;IAMZ;;;;;;;;;OASG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,WAAW,EAAE,GAAG,UAAU;IAYzD;;;;;;OAMG;IACG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAiCtE,4EAA4E;IAC5E,OAAO,CAAC,OAAO;IAOf;;;;;OAKG;YACW,QAAQ;CAoCvB"}
@@ -0,0 +1,145 @@
1
+ import { matchRoute } from './rules.js';
2
+ /** Token ceiling for the classification pass, whose answer is one agent name. */
3
+ const CLASSIFY_MAX_TOKENS = 12;
4
+ /**
5
+ * Routes turns between several agents. See the module description for the
6
+ * stages it decides in.
7
+ */
8
+ export class AgentCoordinator {
9
+ constructor(opts) {
10
+ this.opts = opts;
11
+ /**
12
+ * The agent each conversation is currently with.
13
+ *
14
+ * Keyed by the transcript array itself, so several conversations can be open
15
+ * at once and a discarded one takes its routing state with it.
16
+ */
17
+ this.sticky = new WeakMap();
18
+ /** The classification pass in flight, if any. */
19
+ this.classifying = null;
20
+ /** The agent handling the current turn, so {@link stop} can reach it. */
21
+ this.working = null;
22
+ if (!opts.agents[opts.fallback]) {
23
+ throw new Error(`Coordinator fallback "${opts.fallback}" is not one of its agents.`);
24
+ }
25
+ }
26
+ /**
27
+ * Abandon the turn in flight, whichever stage it has reached.
28
+ *
29
+ * Aborts the classification pass and delegates to the agent handling the
30
+ * turn, which resolves as `'stopped'`. Safe to call when nothing is running.
31
+ */
32
+ stop() {
33
+ this.classifying?.abort();
34
+ this.classifying = null;
35
+ this.working?.stop();
36
+ }
37
+ /**
38
+ * Decide which agent should take a turn, without running it.
39
+ *
40
+ * Covers the rules, sticky and fallback stages. The model stage is skipped,
41
+ * since it is asynchronous and costs a generation.
42
+ *
43
+ * @param goal - The user's message.
44
+ * @param messages - The conversation, consulted for stickiness. Optional.
45
+ * @returns The chosen agent and the stage that chose it.
46
+ */
47
+ route(goal, messages) {
48
+ const decision = matchRoute(goal, this.opts.rules);
49
+ const named = decision.route && this.opts.agents[decision.route] ? decision.route : null;
50
+ if (named)
51
+ return { ...decision, agent: named, via: 'rules', fellBack: false, goal };
52
+ const held = messages && this.sticky.get(messages);
53
+ if (held && this.opts.agents[held]) {
54
+ return { ...decision, agent: held, via: 'sticky', fellBack: false, goal };
55
+ }
56
+ return { ...decision, agent: this.opts.fallback, via: 'fallback', fellBack: true, goal };
57
+ }
58
+ /**
59
+ * Route a turn, run it, and re-route it once if it reached the wrong agent.
60
+ *
61
+ * @param goal - The user's message.
62
+ * @param messages - The live conversation, appended to in place.
63
+ * @returns The chosen agent's {@link TurnResult}.
64
+ */
65
+ async turn(goal, messages) {
66
+ let routed = this.route(goal, messages);
67
+ // The model is only asked when nothing cheaper had an opinion.
68
+ if (routed.via === 'fallback' && this.opts.provider) {
69
+ const guessed = await this.classify(goal);
70
+ if (guessed)
71
+ routed = { ...routed, agent: guessed, via: 'model', fellBack: false };
72
+ }
73
+ this.opts.onRoute?.(routed);
74
+ this.sticky.set(messages, routed.agent);
75
+ this.working = this.opts.agents[routed.agent];
76
+ const result = await this.working.turn(goal, messages);
77
+ // Release stickiness after a refusal, so the next unclear follow-up is not
78
+ // routed back to the one agent known to lack the tools for it.
79
+ if (result.decision === 'unavailable')
80
+ this.sticky.delete(messages);
81
+ if (result.decision !== 'handback' || !result.wanted)
82
+ return result;
83
+ // Exactly one correction: the owner's answer stands whatever it is. Without
84
+ // the cap, two agents that disagree could pass a turn back and forth.
85
+ const owner = this.ownerOf(result.wanted, routed.agent);
86
+ if (!owner)
87
+ return result;
88
+ const moved = { ...routed, agent: owner, via: 'handback', fellBack: false };
89
+ this.opts.onRoute?.(moved);
90
+ this.sticky.set(messages, owner);
91
+ this.working = this.opts.agents[owner];
92
+ return this.working.turn(goal, messages);
93
+ }
94
+ /** The agent holding a tool, ignoring the one that handed the turn back. */
95
+ ownerOf(tool, except) {
96
+ for (const [name, agent] of Object.entries(this.opts.agents)) {
97
+ if (name !== except && agent.owns(tool))
98
+ return name;
99
+ }
100
+ return null;
101
+ }
102
+ /**
103
+ * Ask the model which agent should take the turn.
104
+ *
105
+ * One short generation with no tools. Any reply that does not name a known
106
+ * agent returns null, leaving the fallback in place.
107
+ */
108
+ async classify(goal) {
109
+ const provider = this.opts.provider;
110
+ if (!provider)
111
+ return null;
112
+ const names = Object.keys(this.opts.agents);
113
+ const menu = names
114
+ .map((n) => `- ${n}: ${this.opts.descriptions?.[n] ?? 'handles ' + n + ' requests'}`)
115
+ .join('\n');
116
+ this.classifying = new AbortController();
117
+ try {
118
+ const reply = await provider.complete({
119
+ messages: [
120
+ {
121
+ role: 'system',
122
+ content: 'Choose which assistant should handle the message. ' +
123
+ `Answer with exactly one of these names and nothing else:\n${menu}`,
124
+ },
125
+ { role: 'user', content: goal },
126
+ ],
127
+ tools: [],
128
+ maxTokens: CLASSIFY_MAX_TOKENS,
129
+ signal: this.classifying.signal,
130
+ });
131
+ const said = reply.text.toLowerCase();
132
+ // Longest name first, so a shorter name that is a substring of another
133
+ // does not match in its place.
134
+ const found = [...names]
135
+ .sort((a, b) => b.length - a.length)
136
+ .find((n) => said.includes(n.toLowerCase()));
137
+ return found ?? null;
138
+ }
139
+ catch {
140
+ // Routing must not fail a turn: the fallback covers a failed pass.
141
+ return null;
142
+ }
143
+ }
144
+ }
145
+ //# sourceMappingURL=coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coordinator.js","sourceRoot":"","sources":["../../../src/harness/router/coordinator.ts"],"names":[],"mappings":"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"}