@theokit/sdk-handoff 0.1.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.
- package/CHANGELOG.md +25 -0
- package/LICENSE +201 -0
- package/README.md +96 -0
- package/dist/handoff-D-Ujv-lA.d.cts +131 -0
- package/dist/handoff-D-Ujv-lA.d.ts +131 -0
- package/dist/index.cjs +491 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +84 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +486 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/tool-injector.cjs +339 -0
- package/dist/internal/tool-injector.cjs.map +1 -0
- package/dist/internal/tool-injector.d.cts +38 -0
- package/dist/internal/tool-injector.d.ts +38 -0
- package/dist/internal/tool-injector.js +335 -0
- package/dist/internal/tool-injector.js.map +1 -0
- package/package.json +76 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SDKAgent, Plugin } from '@theokit/sdk';
|
|
2
|
+
import { ZodType } from 'zod';
|
|
3
|
+
import { H as HandoffDescriptor, a as HandoffOptions } from './handoff-D-Ujv-lA.js';
|
|
4
|
+
export { b as HandoffLoopError, c as HandoffNameCollisionError, d as HandoffPairLoopError, e as HandoffReceiverDisposedError, f as HandoffSelfReferenceError } from './handoff-D-Ujv-lA.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Public `Handoff` class — factory for handoff descriptors (Adoption
|
|
8
|
+
* Roadmap #4; ADRs D214-D229).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
*
|
|
12
|
+
* import { Agent, Handoff } from "@theokit/sdk";
|
|
13
|
+
*
|
|
14
|
+
* const billing = await Agent.create({
|
|
15
|
+
* name: "billing",
|
|
16
|
+
* systemPrompt: "You handle billing questions.",
|
|
17
|
+
* model: { id: "openai/gpt-4o-mini" },
|
|
18
|
+
* apiKey: process.env.OPENROUTER_API_KEY,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* const triage = await Agent.create({
|
|
22
|
+
* name: "triage",
|
|
23
|
+
* systemPrompt: "Classify the user's intent and transfer to the right specialist.",
|
|
24
|
+
* model: { id: "openai/gpt-4o-mini" },
|
|
25
|
+
* apiKey: process.env.OPENROUTER_API_KEY,
|
|
26
|
+
* handoffs: [
|
|
27
|
+
* billing, // auto-wrapped as Handoff.create(billing)
|
|
28
|
+
* Handoff.create(supportAgent, { inputFilter: redactCreditCards }),
|
|
29
|
+
* ],
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* @public
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/** Recommended system-prompt prefix for senders (D215 / EC-13). */
|
|
36
|
+
declare const RECOMMENDED_HANDOFF_PROMPT_PREFIX: string;
|
|
37
|
+
declare class Handoff {
|
|
38
|
+
private constructor();
|
|
39
|
+
/**
|
|
40
|
+
* Build a `HandoffDescriptor` for a target agent. Wrap with custom options
|
|
41
|
+
* (filter / inputType / callback / whitelist / etc); pass to
|
|
42
|
+
* `Agent.create({ handoffs: [...] })`.
|
|
43
|
+
*
|
|
44
|
+
* Raw `SDKAgent` instances in `handoffs[]` are auto-wrapped by the runtime
|
|
45
|
+
* — call `Handoff.create()` explicitly only when you need to customize.
|
|
46
|
+
*/
|
|
47
|
+
static create<TInput extends ZodType = ZodType>(target: SDKAgent, options?: HandoffOptions<TInput>): HandoffDescriptor<TInput>;
|
|
48
|
+
/**
|
|
49
|
+
* Plugin-based wiring (SDK 2.x preferred). Wraps `targets` in synthetic
|
|
50
|
+
* `transfer_to_<receiver>` tools and registers them via `ctx.registerTool`
|
|
51
|
+
* at agent init time.
|
|
52
|
+
*
|
|
53
|
+
* Replaces the legacy `Agent.create({ handoffs: [...] })` option (which is
|
|
54
|
+
* still supported as a transitional convenience while sdk-handoff is
|
|
55
|
+
* installed — the framework lazy-imports the tool-injector at runtime).
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const support = await Agent.create({
|
|
59
|
+
* name: "support",
|
|
60
|
+
* plugins: [Handoff.asPlugin({ parentAgentId: "support", targets: [billing] })],
|
|
61
|
+
* });
|
|
62
|
+
*/
|
|
63
|
+
static asPlugin(opts: AsPluginOptions): Plugin;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Options for `Handoff.asPlugin()`. `parentAgentId` defaults to `"anonymous"`;
|
|
67
|
+
* pass the host agent's `name` for correct loop detection in chains.
|
|
68
|
+
*/
|
|
69
|
+
interface AsPluginOptions {
|
|
70
|
+
readonly targets: ReadonlyArray<SDKAgent | HandoffDescriptor>;
|
|
71
|
+
readonly parentAgentId?: string;
|
|
72
|
+
readonly maxHandoffDepth?: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Imperative escape hatch (D225). Useful for tests / programmatic flows
|
|
76
|
+
* that need deterministic handoff without LLM routing.
|
|
77
|
+
*
|
|
78
|
+
* NOTE: this is a STANDALONE helper rather than a method on `SDKAgent`
|
|
79
|
+
* to avoid invasive refactor of the agent class. Behavior is identical
|
|
80
|
+
* to invoking the corresponding synthetic tool would be.
|
|
81
|
+
*/
|
|
82
|
+
declare function handoffTo(sender: SDKAgent, target: SDKAgent, message: string, options?: HandoffOptions): Promise<string>;
|
|
83
|
+
|
|
84
|
+
export { type AsPluginOptions, Handoff, HandoffDescriptor, HandoffOptions, RECOMMENDED_HANDOFF_PROMPT_PREFIX, handoffTo };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { z, toJSONSchema } from 'zod';
|
|
3
|
+
import { definePlugin } from '@theokit/sdk';
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/types/handoff.ts
|
|
16
|
+
var HandoffLoopError, HandoffPairLoopError, HandoffSelfReferenceError, HandoffReceiverDisposedError, HandoffNameCollisionError;
|
|
17
|
+
var init_handoff = __esm({
|
|
18
|
+
"src/types/handoff.ts"() {
|
|
19
|
+
HandoffLoopError = class extends Error {
|
|
20
|
+
name = "HandoffLoopError";
|
|
21
|
+
depth;
|
|
22
|
+
chain;
|
|
23
|
+
constructor(depth, chain) {
|
|
24
|
+
super(
|
|
25
|
+
`Handoff loop exceeded max depth ${depth}. Chain: ${chain.join(" -> ")}. Use Agent.create({ maxHandoffDepth: N }) to raise the cap.`
|
|
26
|
+
);
|
|
27
|
+
this.depth = depth;
|
|
28
|
+
this.chain = chain;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
HandoffPairLoopError = class extends Error {
|
|
32
|
+
name = "HandoffPairLoopError";
|
|
33
|
+
senderAgentId;
|
|
34
|
+
receiverAgentId;
|
|
35
|
+
constructor(senderAgentId, receiverAgentId) {
|
|
36
|
+
super(
|
|
37
|
+
`Handoff loop: ${senderAgentId} -> ${receiverAgentId} already invoked in this send() call. Likely a ping-pong loop; revisit your handoff conditions.`
|
|
38
|
+
);
|
|
39
|
+
this.senderAgentId = senderAgentId;
|
|
40
|
+
this.receiverAgentId = receiverAgentId;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
HandoffSelfReferenceError = class extends Error {
|
|
44
|
+
name = "HandoffSelfReferenceError";
|
|
45
|
+
agentId;
|
|
46
|
+
constructor(agentId) {
|
|
47
|
+
super(
|
|
48
|
+
`Agent "${agentId}" has a self-reference in its handoffs[]. Self-handoff causes infinite recursion; introduce a sibling agent for re-entry.`
|
|
49
|
+
);
|
|
50
|
+
this.agentId = agentId;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
HandoffReceiverDisposedError = class extends Error {
|
|
54
|
+
name = "HandoffReceiverDisposedError";
|
|
55
|
+
receiverAgentId;
|
|
56
|
+
constructor(receiverAgentId) {
|
|
57
|
+
super(
|
|
58
|
+
`Handoff target agent "${receiverAgentId}" is disposed. Don't dispose receivers while their parent is still active.`
|
|
59
|
+
);
|
|
60
|
+
this.receiverAgentId = receiverAgentId;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
HandoffNameCollisionError = class extends Error {
|
|
64
|
+
name = "HandoffNameCollisionError";
|
|
65
|
+
conflictingName;
|
|
66
|
+
constructor(conflictingName) {
|
|
67
|
+
super(
|
|
68
|
+
`Two handoffs share the same tool name "${conflictingName}". Set { toolName } on at least one of them to disambiguate.`
|
|
69
|
+
);
|
|
70
|
+
this.conflictingName = conflictingName;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// src/internal/registry.ts
|
|
77
|
+
var registry_exports = {};
|
|
78
|
+
__export(registry_exports, {
|
|
79
|
+
createChainState: () => createChainState,
|
|
80
|
+
currentDepth: () => currentDepth,
|
|
81
|
+
recordHop: () => recordHop
|
|
82
|
+
});
|
|
83
|
+
function createChainState(rootAgentId, maxDepth) {
|
|
84
|
+
return {
|
|
85
|
+
chain: [rootAgentId],
|
|
86
|
+
seenPairs: /* @__PURE__ */ new Set(),
|
|
87
|
+
maxDepth
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function recordHop(state, senderAgentId, receiverAgentId) {
|
|
91
|
+
const pairKey = `${senderAgentId}->${receiverAgentId}`;
|
|
92
|
+
if (state.seenPairs.has(pairKey)) {
|
|
93
|
+
throw new HandoffPairLoopError(senderAgentId, receiverAgentId);
|
|
94
|
+
}
|
|
95
|
+
state.seenPairs.add(pairKey);
|
|
96
|
+
state.chain.push(receiverAgentId);
|
|
97
|
+
const depth = state.chain.length - 1;
|
|
98
|
+
if (depth > state.maxDepth) {
|
|
99
|
+
throw new HandoffLoopError(state.maxDepth, [...state.chain]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function currentDepth(state) {
|
|
103
|
+
return state.chain.length - 1;
|
|
104
|
+
}
|
|
105
|
+
var init_registry = __esm({
|
|
106
|
+
"src/internal/registry.ts"() {
|
|
107
|
+
init_handoff();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
function getTracer(name, version = "1.0.0") {
|
|
111
|
+
const cached = tracerCache.get(name);
|
|
112
|
+
if (cached !== void 0) return cached.tracer ?? void 0;
|
|
113
|
+
try {
|
|
114
|
+
const r = createRequire(import.meta.url);
|
|
115
|
+
const otel = r("@opentelemetry/api");
|
|
116
|
+
if (otel.trace?.getTracer === void 0) {
|
|
117
|
+
tracerCache.set(name, { tracer: null });
|
|
118
|
+
return void 0;
|
|
119
|
+
}
|
|
120
|
+
const tracer = otel.trace.getTracer(name, version);
|
|
121
|
+
tracerCache.set(name, { tracer });
|
|
122
|
+
return tracer;
|
|
123
|
+
} catch {
|
|
124
|
+
tracerCache.set(name, { tracer: null });
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function safe(fn, fallback) {
|
|
129
|
+
try {
|
|
130
|
+
return fn();
|
|
131
|
+
} catch {
|
|
132
|
+
return fallback;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function startHandoffSpan(attrs) {
|
|
136
|
+
const tracer = getTracer(TRACER_NAME);
|
|
137
|
+
if (tracer === void 0) return NOOP;
|
|
138
|
+
const span = safe(
|
|
139
|
+
() => tracer.startSpan("handoff.transfer", {
|
|
140
|
+
attributes: {
|
|
141
|
+
"handoff.from": attrs.from,
|
|
142
|
+
"handoff.to": attrs.to,
|
|
143
|
+
"handoff.reason": attrs.reason,
|
|
144
|
+
"handoff.depth": attrs.depth,
|
|
145
|
+
"handoff.tool_name": attrs.toolName
|
|
146
|
+
}
|
|
147
|
+
}),
|
|
148
|
+
void 0
|
|
149
|
+
);
|
|
150
|
+
if (span === void 0) return NOOP;
|
|
151
|
+
return {
|
|
152
|
+
setAttribute: (k, v) => safe(() => span.setAttribute(k, v), void 0),
|
|
153
|
+
end: () => safe(() => span.end(), void 0)
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
var tracerCache, TRACER_NAME, NOOP;
|
|
157
|
+
var init_telemetry = __esm({
|
|
158
|
+
"src/internal/telemetry.ts"() {
|
|
159
|
+
tracerCache = /* @__PURE__ */ new Map();
|
|
160
|
+
TRACER_NAME = "theokit-sdk-handoff";
|
|
161
|
+
NOOP = { setAttribute: () => void 0, end: () => void 0 };
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// src/internal/dispatcher.ts
|
|
166
|
+
var dispatcher_exports = {};
|
|
167
|
+
__export(dispatcher_exports, {
|
|
168
|
+
dispatchHandoff: () => dispatchHandoff
|
|
169
|
+
});
|
|
170
|
+
async function safeFilter(filter, history) {
|
|
171
|
+
if (filter === void 0) return history;
|
|
172
|
+
try {
|
|
173
|
+
const result = filter(history);
|
|
174
|
+
return result instanceof Promise ? await result : result;
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (!warnedFilterOnce) {
|
|
177
|
+
warnedFilterOnce = true;
|
|
178
|
+
process.stderr.write(
|
|
179
|
+
`[handoff] inputFilter threw, falling back to full history: ${err instanceof Error ? err.message : String(err)}
|
|
180
|
+
`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return history;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function parseHandoffInput(descriptor, raw) {
|
|
187
|
+
const inputType = descriptor.options.inputType;
|
|
188
|
+
if (inputType === void 0) return void 0;
|
|
189
|
+
const candidate = raw === null || raw === void 0 ? {} : raw;
|
|
190
|
+
return inputType.parse(candidate);
|
|
191
|
+
}
|
|
192
|
+
function isAgentDisposed(agent) {
|
|
193
|
+
const maybe = agent;
|
|
194
|
+
return maybe.disposed === true;
|
|
195
|
+
}
|
|
196
|
+
async function assertHandoffEnabled(descriptor, ctx, receiverAgentId) {
|
|
197
|
+
const opt = descriptor.options.isEnabled;
|
|
198
|
+
let enabled = true;
|
|
199
|
+
if (typeof opt === "boolean") enabled = opt;
|
|
200
|
+
else if (typeof opt === "function") {
|
|
201
|
+
const r = opt(ctx);
|
|
202
|
+
enabled = r instanceof Promise ? await r : r;
|
|
203
|
+
}
|
|
204
|
+
if (!enabled) {
|
|
205
|
+
throw new Error(`Handoff to ${receiverAgentId} is disabled (isEnabled returned false)`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function parseAndValidate(descriptor, rawInputJson) {
|
|
209
|
+
try {
|
|
210
|
+
return parseHandoffInput(descriptor, rawInputJson);
|
|
211
|
+
} catch (err) {
|
|
212
|
+
const detail = err instanceof z.ZodError ? err.issues[0]?.message ?? "schema_invalid" : err instanceof Error ? err.message : String(err);
|
|
213
|
+
throw new Error(`Handoff input validation failed: ${detail}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async function runOnHandoff(descriptor, ctx, parsedInput) {
|
|
217
|
+
const onHandoff = descriptor.options.onHandoff;
|
|
218
|
+
if (onHandoff === void 0) return;
|
|
219
|
+
const result = onHandoff(ctx, parsedInput);
|
|
220
|
+
if (result instanceof Promise) await result;
|
|
221
|
+
}
|
|
222
|
+
function extractUserText(content) {
|
|
223
|
+
if (typeof content === "string") return content;
|
|
224
|
+
if (!Array.isArray(content)) return void 0;
|
|
225
|
+
const text = content.filter((c) => c?.type === "text").map((c) => c.text).join("\n");
|
|
226
|
+
return text.length > 0 ? text : void 0;
|
|
227
|
+
}
|
|
228
|
+
function extractLastUserMessage(history, senderAgentId) {
|
|
229
|
+
for (let i = history.messages.length - 1; i >= 0; i -= 1) {
|
|
230
|
+
const m = history.messages[i];
|
|
231
|
+
if (m?.type !== "user" || m.message?.role !== "user") continue;
|
|
232
|
+
const text = extractUserText(m.message.content);
|
|
233
|
+
if (text !== void 0) return text;
|
|
234
|
+
}
|
|
235
|
+
return `(Handoff from ${senderAgentId} \u2014 no prior user message in history.)`;
|
|
236
|
+
}
|
|
237
|
+
async function dispatchHandoff(args) {
|
|
238
|
+
const { descriptor, senderAgentId, chainState, rawInputJson, history, messageOverride } = args;
|
|
239
|
+
const receiver = descriptor.target;
|
|
240
|
+
if (isAgentDisposed(receiver)) {
|
|
241
|
+
throw new HandoffReceiverDisposedError(receiver.agentId);
|
|
242
|
+
}
|
|
243
|
+
const depthAfterThisHop = chainState.chain.length;
|
|
244
|
+
const ctx = {
|
|
245
|
+
senderAgentId,
|
|
246
|
+
receiverAgentId: receiver.agentId,
|
|
247
|
+
currentDepth: depthAfterThisHop,
|
|
248
|
+
chain: [...chainState.chain, receiver.agentId]
|
|
249
|
+
};
|
|
250
|
+
await assertHandoffEnabled(descriptor, ctx, receiver.agentId);
|
|
251
|
+
const parsedInput = parseAndValidate(descriptor, rawInputJson);
|
|
252
|
+
await runOnHandoff(descriptor, ctx, parsedInput);
|
|
253
|
+
const filteredHistory = await safeFilter(descriptor.options.inputFilter, history);
|
|
254
|
+
recordHop(chainState, senderAgentId, receiver.agentId);
|
|
255
|
+
const lastUserMessage = messageOverride ?? extractLastUserMessage(filteredHistory, senderAgentId);
|
|
256
|
+
const reason = extractReason(parsedInput);
|
|
257
|
+
const span = startHandoffSpan({
|
|
258
|
+
from: senderAgentId,
|
|
259
|
+
to: receiver.agentId,
|
|
260
|
+
reason,
|
|
261
|
+
depth: depthAfterThisHop,
|
|
262
|
+
toolName: descriptor.resolvedToolName
|
|
263
|
+
});
|
|
264
|
+
try {
|
|
265
|
+
const run = await receiver.send(lastUserMessage);
|
|
266
|
+
const result = await run.wait();
|
|
267
|
+
const reply = buildReply(result, receiver.agentId);
|
|
268
|
+
return {
|
|
269
|
+
reply,
|
|
270
|
+
result: {
|
|
271
|
+
from: senderAgentId,
|
|
272
|
+
to: receiver.agentId,
|
|
273
|
+
depth: depthAfterThisHop,
|
|
274
|
+
toolName: descriptor.resolvedToolName,
|
|
275
|
+
...reason !== "" ? { reasonFromLlm: reason } : {}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
} finally {
|
|
279
|
+
span.end();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function extractReason(parsedInput) {
|
|
283
|
+
if (typeof parsedInput !== "object" || parsedInput === null) return "";
|
|
284
|
+
if (!("reason" in parsedInput)) return "";
|
|
285
|
+
return String(parsedInput.reason ?? "");
|
|
286
|
+
}
|
|
287
|
+
function buildReply(result, receiverAgentId) {
|
|
288
|
+
if (result.status === "finished" && result.result !== void 0) return result.result;
|
|
289
|
+
const suffix = result.error !== void 0 ? `: ${result.error.message}` : "";
|
|
290
|
+
return `(Handoff target ${receiverAgentId} returned status=${result.status}${suffix})`;
|
|
291
|
+
}
|
|
292
|
+
var warnedFilterOnce;
|
|
293
|
+
var init_dispatcher = __esm({
|
|
294
|
+
"src/internal/dispatcher.ts"() {
|
|
295
|
+
init_handoff();
|
|
296
|
+
init_registry();
|
|
297
|
+
init_telemetry();
|
|
298
|
+
warnedFilterOnce = false;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
function toJsonSchema(schema, options = { unrepresentable: "any" }) {
|
|
302
|
+
return toJSONSchema(schema, options);
|
|
303
|
+
}
|
|
304
|
+
var init_to_json_schema = __esm({
|
|
305
|
+
"src/internal/to-json-schema.ts"() {
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// src/internal/tool-injector.ts
|
|
310
|
+
var tool_injector_exports = {};
|
|
311
|
+
__export(tool_injector_exports, {
|
|
312
|
+
buildHandoffTool: () => buildHandoffTool,
|
|
313
|
+
normalizeHandoffs: () => normalizeHandoffs
|
|
314
|
+
});
|
|
315
|
+
function normalizeHandoffs(parentAgentId, entries) {
|
|
316
|
+
if (entries.length === 0) return [];
|
|
317
|
+
const out = [];
|
|
318
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
319
|
+
for (const entry of entries) {
|
|
320
|
+
const isDescriptor = typeof entry === "object" && entry !== null && "target" in entry && "options" in entry && "resolvedToolName" in entry;
|
|
321
|
+
const descriptor = isDescriptor ? entry : autoWrap(entry);
|
|
322
|
+
if (descriptor.target.agentId === parentAgentId) {
|
|
323
|
+
throw new HandoffSelfReferenceError(parentAgentId);
|
|
324
|
+
}
|
|
325
|
+
const name = descriptor.resolvedToolName;
|
|
326
|
+
if (seenNames.has(name)) {
|
|
327
|
+
throw new HandoffNameCollisionError(name);
|
|
328
|
+
}
|
|
329
|
+
seenNames.add(name);
|
|
330
|
+
out.push({ descriptor });
|
|
331
|
+
}
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
334
|
+
function autoWrap(agent) {
|
|
335
|
+
const name = resolveTargetName(agent);
|
|
336
|
+
return {
|
|
337
|
+
target: agent,
|
|
338
|
+
options: {},
|
|
339
|
+
resolvedToolName: `transfer_to_${name}`
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function resolveTargetName(agent) {
|
|
343
|
+
const candidate = agent.name ?? agent.agentId ?? "anonymous";
|
|
344
|
+
return slugify(candidate);
|
|
345
|
+
}
|
|
346
|
+
function slugify(input) {
|
|
347
|
+
return input.replace(/^agent-/i, "").replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) || "anonymous";
|
|
348
|
+
}
|
|
349
|
+
function buildHandoffTool(parentAgentId, descriptor, maxHandoffDepth) {
|
|
350
|
+
const description = descriptor.options.toolDescription ?? `Transfer the conversation to the ${descriptor.target.agentId} agent. Use this when the user's request matches their specialty.`;
|
|
351
|
+
const inputZod = descriptor.options.inputType ?? z.object({
|
|
352
|
+
reason: z.string().optional().describe("Brief reason for the transfer (one short sentence).")
|
|
353
|
+
});
|
|
354
|
+
const inputSchema = toJsonSchema(inputZod);
|
|
355
|
+
return {
|
|
356
|
+
name: descriptor.resolvedToolName,
|
|
357
|
+
description,
|
|
358
|
+
inputSchema,
|
|
359
|
+
handler: async (input) => {
|
|
360
|
+
const chainState = createChainState(parentAgentId, maxHandoffDepth);
|
|
361
|
+
try {
|
|
362
|
+
const { reply, result } = await dispatchHandoff({
|
|
363
|
+
descriptor,
|
|
364
|
+
senderAgentId: parentAgentId,
|
|
365
|
+
chainState,
|
|
366
|
+
rawInputJson: input,
|
|
367
|
+
history: { messages: [] }
|
|
368
|
+
// v1: history replay deferred
|
|
369
|
+
});
|
|
370
|
+
return JSON.stringify({
|
|
371
|
+
ok: true,
|
|
372
|
+
transferred_to: result.to,
|
|
373
|
+
depth: result.depth,
|
|
374
|
+
reply
|
|
375
|
+
});
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return JSON.stringify({
|
|
378
|
+
ok: false,
|
|
379
|
+
error: err instanceof Error ? err.name : "HandoffError",
|
|
380
|
+
message: err instanceof Error ? err.message : String(err)
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
var init_tool_injector = __esm({
|
|
387
|
+
"src/internal/tool-injector.ts"() {
|
|
388
|
+
init_handoff();
|
|
389
|
+
init_dispatcher();
|
|
390
|
+
init_registry();
|
|
391
|
+
init_to_json_schema();
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
var RECOMMENDED_HANDOFF_PROMPT_PREFIX = `
|
|
395
|
+
You can transfer the conversation to other specialist agents when their
|
|
396
|
+
expertise matches the user's request. Invoke the appropriate
|
|
397
|
+
transfer_to_<agent> tool with a short reason. The receiving agent will
|
|
398
|
+
take over the conversation; do not duplicate their work.
|
|
399
|
+
`.trim();
|
|
400
|
+
var Handoff = class {
|
|
401
|
+
constructor() {
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Build a `HandoffDescriptor` for a target agent. Wrap with custom options
|
|
405
|
+
* (filter / inputType / callback / whitelist / etc); pass to
|
|
406
|
+
* `Agent.create({ handoffs: [...] })`.
|
|
407
|
+
*
|
|
408
|
+
* Raw `SDKAgent` instances in `handoffs[]` are auto-wrapped by the runtime
|
|
409
|
+
* — call `Handoff.create()` explicitly only when you need to customize.
|
|
410
|
+
*/
|
|
411
|
+
static create(target, options = {}) {
|
|
412
|
+
if (target === void 0 || target === null) {
|
|
413
|
+
throw new Error("Handoff.create: target agent is required");
|
|
414
|
+
}
|
|
415
|
+
if (typeof target.send !== "function") {
|
|
416
|
+
throw new Error("Handoff.create: target must be an SDKAgent instance");
|
|
417
|
+
}
|
|
418
|
+
const resolvedToolName = options.toolName ?? `transfer_to_${slugifyName(target)}`;
|
|
419
|
+
return {
|
|
420
|
+
target,
|
|
421
|
+
options,
|
|
422
|
+
resolvedToolName
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Plugin-based wiring (SDK 2.x preferred). Wraps `targets` in synthetic
|
|
427
|
+
* `transfer_to_<receiver>` tools and registers them via `ctx.registerTool`
|
|
428
|
+
* at agent init time.
|
|
429
|
+
*
|
|
430
|
+
* Replaces the legacy `Agent.create({ handoffs: [...] })` option (which is
|
|
431
|
+
* still supported as a transitional convenience while sdk-handoff is
|
|
432
|
+
* installed — the framework lazy-imports the tool-injector at runtime).
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* const support = await Agent.create({
|
|
436
|
+
* name: "support",
|
|
437
|
+
* plugins: [Handoff.asPlugin({ parentAgentId: "support", targets: [billing] })],
|
|
438
|
+
* });
|
|
439
|
+
*/
|
|
440
|
+
static asPlugin(opts) {
|
|
441
|
+
const parent = opts.parentAgentId ?? "anonymous";
|
|
442
|
+
const maxDepth = opts.maxHandoffDepth ?? 5;
|
|
443
|
+
const targets = opts.targets;
|
|
444
|
+
return definePlugin({
|
|
445
|
+
name: `handoff-${parent}`,
|
|
446
|
+
version: "1.0.0",
|
|
447
|
+
kind: "general",
|
|
448
|
+
register(ctx) {
|
|
449
|
+
if (maxDepth === 0 || targets.length === 0) return;
|
|
450
|
+
void (async () => {
|
|
451
|
+
const { normalizeHandoffs: normalizeHandoffs2, buildHandoffTool: buildHandoffTool2 } = await Promise.resolve().then(() => (init_tool_injector(), tool_injector_exports));
|
|
452
|
+
const normalized = normalizeHandoffs2(parent, targets);
|
|
453
|
+
for (const { descriptor } of normalized) {
|
|
454
|
+
ctx.registerTool(buildHandoffTool2(parent, descriptor, maxDepth));
|
|
455
|
+
}
|
|
456
|
+
})();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
function slugifyName(agent) {
|
|
462
|
+
const candidate = agent.name ?? agent.agentId ?? "anonymous";
|
|
463
|
+
return candidate.replace(/^agent-/i, "").replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64) || "anonymous";
|
|
464
|
+
}
|
|
465
|
+
async function handoffTo(sender, target, message, options = {}) {
|
|
466
|
+
const descriptor = Handoff.create(target, options);
|
|
467
|
+
const { dispatchHandoff: dispatchHandoff2 } = await Promise.resolve().then(() => (init_dispatcher(), dispatcher_exports));
|
|
468
|
+
const { createChainState: createChainState2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
469
|
+
const chainState = createChainState2(sender.agentId, 5);
|
|
470
|
+
const { reply } = await dispatchHandoff2({
|
|
471
|
+
descriptor,
|
|
472
|
+
senderAgentId: sender.agentId,
|
|
473
|
+
chainState,
|
|
474
|
+
rawInputJson: void 0,
|
|
475
|
+
history: { messages: [] },
|
|
476
|
+
messageOverride: message
|
|
477
|
+
});
|
|
478
|
+
return reply;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/index.ts
|
|
482
|
+
init_handoff();
|
|
483
|
+
|
|
484
|
+
export { Handoff, HandoffLoopError, HandoffNameCollisionError, HandoffPairLoopError, HandoffReceiverDisposedError, HandoffSelfReferenceError, RECOMMENDED_HANDOFF_PROMPT_PREFIX, handoffTo };
|
|
485
|
+
//# sourceMappingURL=index.js.map
|
|
486
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types/handoff.ts","../src/internal/registry.ts","../src/internal/telemetry.ts","../src/internal/dispatcher.ts","../src/internal/to-json-schema.ts","../src/internal/tool-injector.ts","../src/handoff.ts","../src/index.ts"],"names":["z","normalizeHandoffs","buildHandoffTool","dispatchHandoff","createChainState"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Public types for `Agent.create({ handoffs })` + `Handoff.create()` +\n * `Agent.handoffTo()` (Adoption Roadmap #4; ADRs D214-D229).\n *\n * Pattern: handoff-as-tool. Each handoff destination becomes a synthetic\n * `transfer_to_<receiver>` function tool exposed to the LLM. Runtime\n * intercepts the tool call and routes the next turn to the receiver.\n *\n * T4.1 follow-up (cycle #4 closed): `HandoffDescriptor` + its leaf-friendly\n * sibling types now live in `./handoff-descriptor.ts` (generic over\n * `TAgent`). This module re-exports the leaf types pinned to `SDKAgent`,\n * keeps the runtime error classes, and removes the back-edge to `agent.ts`.\n *\n * @public\n */\n\nimport type { SDKAgent } from \"@theokit/sdk\";\nimport type { ZodType } from \"zod\";\nimport type {\n HandoffContext,\n HandoffDescriptor as HandoffDescriptorGeneric,\n HandoffHistory,\n HandoffOptions,\n HandoffResult,\n} from \"./handoff-descriptor.js\";\n\nexport type { HandoffContext, HandoffHistory, HandoffOptions, HandoffResult };\n\n/**\n * `HandoffDescriptor` pinned to `SDKAgent` — back-compat shape for callers\n * that imported `import type { HandoffDescriptor } from \"@theokit/sdk\"`\n * before T4.1 follow-up.\n *\n * @public\n */\nexport type HandoffDescriptor<TInput extends ZodType = ZodType> = HandoffDescriptorGeneric<\n TInput,\n SDKAgent\n>;\n\n/** Throw when handoff depth exceeds `maxHandoffDepth` (default 5; D218). */\nexport class HandoffLoopError extends Error {\n override readonly name = \"HandoffLoopError\";\n readonly depth: number;\n readonly chain: ReadonlyArray<string>;\n constructor(depth: number, chain: ReadonlyArray<string>) {\n super(\n `Handoff loop exceeded max depth ${depth}. Chain: ${chain.join(\" -> \")}. ` +\n `Use Agent.create({ maxHandoffDepth: N }) to raise the cap.`,\n );\n this.depth = depth;\n this.chain = chain;\n }\n}\n\n/** Throw when the same (sender, receiver) pair invoked twice in one send() (D221). */\nexport class HandoffPairLoopError extends Error {\n override readonly name = \"HandoffPairLoopError\";\n readonly senderAgentId: string;\n readonly receiverAgentId: string;\n constructor(senderAgentId: string, receiverAgentId: string) {\n super(\n `Handoff loop: ${senderAgentId} -> ${receiverAgentId} already invoked in this send() call. ` +\n `Likely a ping-pong loop; revisit your handoff conditions.`,\n );\n this.senderAgentId = senderAgentId;\n this.receiverAgentId = receiverAgentId;\n }\n}\n\n/** Throw when an agent's `handoffs[]` includes a self-reference (EC-6). */\nexport class HandoffSelfReferenceError extends Error {\n override readonly name = \"HandoffSelfReferenceError\";\n readonly agentId: string;\n constructor(agentId: string) {\n super(\n `Agent \"${agentId}\" has a self-reference in its handoffs[]. ` +\n `Self-handoff causes infinite recursion; introduce a sibling agent for re-entry.`,\n );\n this.agentId = agentId;\n }\n}\n\n/** Throw when receiver is disposed at dispatch time (EC-5). */\nexport class HandoffReceiverDisposedError extends Error {\n override readonly name = \"HandoffReceiverDisposedError\";\n readonly receiverAgentId: string;\n constructor(receiverAgentId: string) {\n super(\n `Handoff target agent \"${receiverAgentId}\" is disposed. ` +\n `Don't dispose receivers while their parent is still active.`,\n );\n this.receiverAgentId = receiverAgentId;\n }\n}\n\n/** Throw when two handoffs in the same parent collide on tool name (D215). */\nexport class HandoffNameCollisionError extends Error {\n override readonly name = \"HandoffNameCollisionError\";\n readonly conflictingName: string;\n constructor(conflictingName: string) {\n super(\n `Two handoffs share the same tool name \"${conflictingName}\". ` +\n `Set { toolName } on at least one of them to disambiguate.`,\n );\n this.conflictingName = conflictingName;\n }\n}\n","/**\n * Handoff registry — pure state container per `Agent` instance.\n *\n * Holds the active dispatch chain (for depth + pair tracking) across the\n * lifetime of a single `agent.send()` call. Cleared between calls.\n *\n * @internal\n */\n\nimport { HandoffLoopError, HandoffPairLoopError } from \"../types/handoff.js\";\n\nexport interface HandoffChainState {\n /** Ordered chain of agentIds traversed so far (oldest first). */\n readonly chain: string[];\n /** Set of \"<sender>-><receiver>\" keys for pair-level loop detection (D221). */\n readonly seenPairs: Set<string>;\n /** Caller-supplied max depth (D218). */\n readonly maxDepth: number;\n}\n\nexport function createChainState(rootAgentId: string, maxDepth: number): HandoffChainState {\n return {\n chain: [rootAgentId],\n seenPairs: new Set(),\n maxDepth,\n };\n}\n\n/**\n * Record a handoff hop. Throws on depth-exceed (D218) or pair-loop (D221).\n * Mutates the state in place.\n */\nexport function recordHop(\n state: HandoffChainState,\n senderAgentId: string,\n receiverAgentId: string,\n): void {\n const pairKey = `${senderAgentId}->${receiverAgentId}`;\n if (state.seenPairs.has(pairKey)) {\n throw new HandoffPairLoopError(senderAgentId, receiverAgentId);\n }\n state.seenPairs.add(pairKey);\n state.chain.push(receiverAgentId);\n // chain.length = nodes; depth = hops = chain.length - 1.\n const depth = state.chain.length - 1;\n if (depth > state.maxDepth) {\n throw new HandoffLoopError(state.maxDepth, [...state.chain]);\n }\n}\n\nexport function currentDepth(state: HandoffChainState): number {\n return state.chain.length - 1;\n}\n","/**\n * D220 — Lazy-loaded OTel `handoff.transfer` span emitter.\n *\n * @internal\n */\n\n// Inline tracer-loader (same workaround as @theokit/sdk-cache/internal/telemetry.ts):\n// rollup-plugin-dts emits incomplete index.d.ts for newly-modified internal/ barrels\n// in @theokit/sdk. Runtime via the sub-path works; TypeScript users hit TS2305.\n// Inlining keeps sdk-handoff self-contained for the (small) observability hook.\nimport { createRequire } from \"node:module\";\n\ninterface SpanLike {\n setAttribute(key: string, value: string | number | boolean): SpanLike;\n end(): void;\n}\n\nconst _NOOP_SPAN: SpanLike = {\n setAttribute: () => _NOOP_SPAN,\n end: () => undefined,\n};\n\ninterface TracerLike {\n startSpan(\n name: string,\n options?: { attributes?: Record<string, string | number | boolean> },\n ): SpanLike;\n}\n\ninterface TracerCacheEntry {\n tracer: TracerLike | null;\n}\nconst tracerCache = new Map<string, TracerCacheEntry>();\n\nfunction getTracer(name: string, version = \"1.0.0\"): TracerLike | undefined {\n const cached = tracerCache.get(name);\n if (cached !== undefined) return cached.tracer ?? undefined;\n try {\n const r = createRequire(import.meta.url);\n const otel = r(\"@opentelemetry/api\") as {\n trace?: { getTracer: (n: string, v?: string) => TracerLike };\n };\n if (otel.trace?.getTracer === undefined) {\n tracerCache.set(name, { tracer: null });\n return undefined;\n }\n const tracer = otel.trace.getTracer(name, version);\n tracerCache.set(name, { tracer });\n return tracer;\n } catch {\n tracerCache.set(name, { tracer: null });\n return undefined;\n }\n}\n\nfunction resetTracerCacheForTests(): void {\n tracerCache.clear();\n}\n\nconst TRACER_NAME = \"theokit-sdk-handoff\";\n\ninterface HandoffSpanHandle {\n setAttribute(key: string, value: string | number | boolean): void;\n end(): void;\n}\n\nconst NOOP: HandoffSpanHandle = { setAttribute: () => undefined, end: () => undefined };\n\nfunction safe<T>(fn: () => T, fallback: T): T {\n try {\n return fn();\n } catch {\n return fallback;\n }\n}\n\nexport function startHandoffSpan(attrs: {\n from: string;\n to: string;\n reason: string;\n depth: number;\n toolName: string;\n}): HandoffSpanHandle {\n const tracer = getTracer(TRACER_NAME);\n if (tracer === undefined) return NOOP;\n const span: SpanLike | undefined = safe(\n () =>\n tracer.startSpan(\"handoff.transfer\", {\n attributes: {\n \"handoff.from\": attrs.from,\n \"handoff.to\": attrs.to,\n \"handoff.reason\": attrs.reason,\n \"handoff.depth\": attrs.depth,\n \"handoff.tool_name\": attrs.toolName,\n },\n }),\n undefined,\n );\n if (span === undefined) return NOOP;\n return {\n setAttribute: (k, v) => safe(() => span.setAttribute(k, v), undefined),\n end: () => safe(() => span.end(), undefined),\n };\n}\n\n/** Test-only — reset cached OTel handle. */\nfunction __resetHandoffOtelCacheForTests(): void {\n resetTracerCacheForTests();\n}\n","/**\n * Handoff dispatch orchestration.\n *\n * Pragmatic v1: when a handoff fires, the sender calls `receiver.send()`\n * with the (optionally filtered) history. The receiver's reply is returned\n * to the sender, which captures it as the answer to the user's question.\n *\n * NOTE: this is NOT pure peer-to-peer (the sender stays on the call stack\n * until the receiver returns). Pure intercept-and-swap requires deeper\n * agent-loop refactor; deferred to v2. v1 still validates the user-facing\n * value: \"agent A reasoned about routing, agent B answered.\"\n *\n * @internal\n */\n\nimport type { SDKAgent } from \"@theokit/sdk\";\nimport { z } from \"zod\";\nimport type {\n HandoffContext,\n HandoffDescriptor,\n HandoffHistory,\n HandoffResult,\n} from \"../types/handoff.js\";\nimport { HandoffReceiverDisposedError } from \"../types/handoff.js\";\nimport { type HandoffChainState, recordHop } from \"./registry.js\";\nimport { startHandoffSpan } from \"./telemetry.js\";\n\n/**\n * EC-2 / D228 — `safeFilter` wraps `inputFilter`. On exception, falls back\n * to the un-filtered history and warns to stderr once per process.\n */\nlet warnedFilterOnce = false;\nasync function safeFilter(\n filter: ((h: HandoffHistory) => HandoffHistory | Promise<HandoffHistory>) | undefined,\n history: HandoffHistory,\n): Promise<HandoffHistory> {\n if (filter === undefined) return history;\n try {\n const result = filter(history);\n return result instanceof Promise ? await result : result;\n } catch (err) {\n if (!warnedFilterOnce) {\n warnedFilterOnce = true;\n process.stderr.write(\n `[handoff] inputFilter threw, falling back to full history: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n }\n return history;\n }\n}\n\n/**\n * EC-4 / D229 — parse the LLM-provided JSON args. Returns undefined when\n * no `inputType` set; returns parsed value otherwise (default to `{}` on\n * empty/null input before Zod refinements).\n */\nfunction parseHandoffInput(descriptor: HandoffDescriptor, raw: unknown): unknown {\n const inputType = descriptor.options.inputType;\n if (inputType === undefined) return undefined;\n const candidate = raw === null || raw === undefined ? {} : raw;\n return inputType.parse(candidate);\n}\n\nfunction isAgentDisposed(agent: SDKAgent): boolean {\n // SDKAgent doesn't expose `disposed` publicly; check via duck-typing on\n // a known internal flag. Safe fallback: if we can't tell, assume alive.\n const maybe = agent as unknown as { disposed?: boolean };\n return maybe.disposed === true;\n}\n\n/**\n * Run a single handoff hop. Returns the receiver's reply text.\n *\n * Algorithm:\n * 1. EC-5: refuse if receiver disposed.\n * 2. Build HandoffContext.\n * 3. Check isEnabled() — if false, refuse with a clear error message.\n * 4. Parse inputType (D229).\n * 5. Run onHandoff(ctx, parsed) — throw aborts (D227).\n * 6. Apply inputFilter (safeFilter — D228).\n * 7. Record hop in chain state (depth + pair guards).\n * 8. Open OTel span (D220).\n * 9. Receiver: build the user-facing message and `await receiver.send(msg).then(wait)`.\n * 10. Close span + return reply.\n */\nasync function assertHandoffEnabled(\n descriptor: HandoffDescriptor,\n ctx: HandoffContext,\n receiverAgentId: string,\n): Promise<void> {\n const opt = descriptor.options.isEnabled;\n let enabled = true;\n if (typeof opt === \"boolean\") enabled = opt;\n else if (typeof opt === \"function\") {\n const r = opt(ctx);\n enabled = r instanceof Promise ? await r : r;\n }\n if (!enabled) {\n throw new Error(`Handoff to ${receiverAgentId} is disabled (isEnabled returned false)`);\n }\n}\n\nfunction parseAndValidate(descriptor: HandoffDescriptor, rawInputJson: unknown): unknown {\n try {\n return parseHandoffInput(descriptor, rawInputJson);\n } catch (err) {\n const detail =\n err instanceof z.ZodError\n ? (err.issues[0]?.message ?? \"schema_invalid\")\n : err instanceof Error\n ? err.message\n : String(err);\n throw new Error(`Handoff input validation failed: ${detail}`);\n }\n}\n\nasync function runOnHandoff(\n descriptor: HandoffDescriptor,\n ctx: HandoffContext,\n parsedInput: unknown,\n): Promise<void> {\n const onHandoff = descriptor.options.onHandoff;\n if (onHandoff === undefined) return;\n // biome-ignore lint/suspicious/noExplicitAny: parsedInput is typed unknown by design.\n const result = onHandoff(ctx, parsedInput as any);\n if (result instanceof Promise) await result;\n}\n\nfunction extractUserText(content: unknown): string | undefined {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return undefined;\n const text = content\n .filter((c): c is { type: \"text\"; text: string } => (c as { type?: string })?.type === \"text\")\n .map((c) => c.text)\n .join(\"\\n\");\n return text.length > 0 ? text : undefined;\n}\n\nfunction extractLastUserMessage(history: HandoffHistory, senderAgentId: string): string {\n for (let i = history.messages.length - 1; i >= 0; i -= 1) {\n const m = history.messages[i] as {\n type?: string;\n message?: { role?: string; content?: unknown };\n };\n if (m?.type !== \"user\" || m.message?.role !== \"user\") continue;\n const text = extractUserText(m.message.content);\n if (text !== undefined) return text;\n }\n return `(Handoff from ${senderAgentId} — no prior user message in history.)`;\n}\n\nexport async function dispatchHandoff(args: {\n descriptor: HandoffDescriptor;\n senderAgentId: string;\n chainState: HandoffChainState;\n rawInputJson: unknown;\n /** The conversation so far (history wrapper). v1: just the LAST user message. */\n history: HandoffHistory;\n /** Override the message text sent to the receiver. Used by `Agent.handoffTo` imperative. */\n messageOverride?: string;\n}): Promise<{ reply: string; result: HandoffResult }> {\n const { descriptor, senderAgentId, chainState, rawInputJson, history, messageOverride } = args;\n const receiver = descriptor.target;\n\n if (isAgentDisposed(receiver)) {\n throw new HandoffReceiverDisposedError(receiver.agentId);\n }\n\n const depthAfterThisHop = chainState.chain.length;\n const ctx: HandoffContext = {\n senderAgentId,\n receiverAgentId: receiver.agentId,\n currentDepth: depthAfterThisHop,\n chain: [...chainState.chain, receiver.agentId],\n };\n\n await assertHandoffEnabled(descriptor, ctx, receiver.agentId);\n const parsedInput = parseAndValidate(descriptor, rawInputJson);\n await runOnHandoff(descriptor, ctx, parsedInput);\n\n // Filter history (D228 — resilient)\n const filteredHistory = await safeFilter(descriptor.options.inputFilter, history);\n\n // Record hop — may throw HandoffLoopError or HandoffPairLoopError\n recordHop(chainState, senderAgentId, receiver.agentId);\n\n const lastUserMessage = messageOverride ?? extractLastUserMessage(filteredHistory, senderAgentId);\n const reason = extractReason(parsedInput);\n\n const span = startHandoffSpan({\n from: senderAgentId,\n to: receiver.agentId,\n reason,\n depth: depthAfterThisHop,\n toolName: descriptor.resolvedToolName,\n });\n\n try {\n const run = await receiver.send(lastUserMessage);\n const result = await run.wait();\n const reply = buildReply(result, receiver.agentId);\n return {\n reply,\n result: {\n from: senderAgentId,\n to: receiver.agentId,\n depth: depthAfterThisHop,\n toolName: descriptor.resolvedToolName,\n ...(reason !== \"\" ? { reasonFromLlm: reason } : {}),\n },\n };\n } finally {\n span.end();\n }\n}\n\nfunction extractReason(parsedInput: unknown): string {\n if (typeof parsedInput !== \"object\" || parsedInput === null) return \"\";\n if (!(\"reason\" in parsedInput)) return \"\";\n return String((parsedInput as { reason: unknown }).reason ?? \"\");\n}\n\nfunction buildReply(\n result: { status: string; result?: string; error?: { message: string } },\n receiverAgentId: string,\n): string {\n if (result.status === \"finished\" && result.result !== undefined) return result.result;\n const suffix = result.error !== undefined ? `: ${result.error.message}` : \"\";\n return `(Handoff target ${receiverAgentId} returned status=${result.status}${suffix})`;\n}\n","/**\n * Zod v4 → JSON Schema adapter for sdk-handoff.\n *\n * Uses Zod v4's native `z.toJSONSchema()` directly. v3 fallback removed\n * after zod-v4-migration plan (ADR D2).\n *\n * @internal\n */\n\nimport { toJSONSchema } from \"zod\";\n\ninterface ToJsonSchemaOptions {\n /** `\"any\"` keeps transforms/refinements as `{}` (loose). Default: `\"any\"`. */\n unrepresentable?: \"any\" | \"throw\";\n}\n\n/**\n * Convert a Zod schema to a JSON Schema object via Zod v4 native.\n *\n * @internal\n */\nexport function toJsonSchema(\n schema: unknown,\n options: ToJsonSchemaOptions = { unrepresentable: \"any\" },\n): Record<string, unknown> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- generic ZodType erasure\n return toJSONSchema(schema as any, options) as Record<string, unknown>;\n}\n","/**\n * Convert `handoffs[]` entries into synthetic `transfer_to_<receiver>` tools\n * for injection into the agent's tool registry at construction time.\n *\n * The synthesized tool's handler calls `dispatchHandoff` internally and\n * returns the receiver's reply as `tool_result`. v1 trade-off documented\n * in dispatcher.ts.\n *\n * @internal\n */\n\nimport type { CustomTool, SDKAgent } from \"@theokit/sdk\";\nimport { z } from \"zod\";\nimport {\n type HandoffDescriptor,\n HandoffNameCollisionError,\n HandoffSelfReferenceError,\n} from \"../types/handoff.js\";\nimport { dispatchHandoff } from \"./dispatcher.js\";\nimport { createChainState } from \"./registry.js\";\nimport { toJsonSchema } from \"./to-json-schema.js\";\n\ninterface NormalizedHandoff {\n descriptor: HandoffDescriptor;\n}\n\n/**\n * Normalize each `handoffs[]` entry to a `HandoffDescriptor`. Raw `SDKAgent`\n * instances are auto-wrapped with default options. Validates:\n * - EC-6: no self-reference (would cause infinite recursion).\n * - D215: resolved tool names must be unique.\n */\nexport function normalizeHandoffs(\n parentAgentId: string,\n entries: ReadonlyArray<SDKAgent | HandoffDescriptor>,\n): NormalizedHandoff[] {\n if (entries.length === 0) return [];\n const out: NormalizedHandoff[] = [];\n const seenNames = new Set<string>();\n for (const entry of entries) {\n // Detect raw Agent vs HandoffDescriptor by presence of `.target`.\n const isDescriptor =\n typeof entry === \"object\" &&\n entry !== null &&\n \"target\" in entry &&\n \"options\" in entry &&\n \"resolvedToolName\" in entry;\n const descriptor = isDescriptor ? (entry as HandoffDescriptor) : autoWrap(entry as SDKAgent);\n if (descriptor.target.agentId === parentAgentId) {\n throw new HandoffSelfReferenceError(parentAgentId);\n }\n const name = descriptor.resolvedToolName;\n if (seenNames.has(name)) {\n throw new HandoffNameCollisionError(name);\n }\n seenNames.add(name);\n out.push({ descriptor });\n }\n return out;\n}\n\nfunction autoWrap(agent: SDKAgent): HandoffDescriptor {\n const name = resolveTargetName(agent);\n return {\n target: agent,\n options: {},\n resolvedToolName: `transfer_to_${name}`,\n };\n}\n\nfunction resolveTargetName(agent: SDKAgent): string {\n // Prefer a `name` field if exposed; fall back to a short agentId slug.\n const candidate = (agent as unknown as { name?: string }).name ?? agent.agentId ?? \"anonymous\";\n return slugify(candidate);\n}\n\nfunction slugify(input: string): string {\n return (\n input\n .replace(/^agent-/i, \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64) || \"anonymous\"\n );\n}\n\n/**\n * Build a `CustomTool` for one handoff descriptor. The handler dispatches\n * the handoff using a fresh chain state per `send()`-level invocation.\n *\n * NOTE: this v1 builds a NEW chain state per tool invocation. Pure\n * cross-tool depth tracking within one send() requires per-Agent context\n * — deferred. The single-flight pair guard catches direct ping-pong even\n * without cross-invocation chain (since each call wraps the same depth\n * counter from 1).\n */\nexport function buildHandoffTool(\n parentAgentId: string,\n descriptor: HandoffDescriptor,\n maxHandoffDepth: number,\n): CustomTool {\n const description =\n descriptor.options.toolDescription ??\n `Transfer the conversation to the ${descriptor.target.agentId} agent. ` +\n `Use this when the user's request matches their specialty.`;\n\n const inputZod =\n descriptor.options.inputType ??\n z.object({\n reason: z.string().optional().describe(\"Brief reason for the transfer (one short sentence).\"),\n });\n // CustomTool.inputSchema expects a JSON schema (Record<string, unknown>),\n // not the raw Zod type. Convert lazily so we don't fail when Zod is missing.\n // Universal Zod 3+4 conversion (feature-detects native v4, falls back to lib on v3).\n const inputSchema = toJsonSchema(inputZod);\n\n return {\n name: descriptor.resolvedToolName,\n description,\n inputSchema,\n handler: async (input: unknown): Promise<string> => {\n const chainState = createChainState(parentAgentId, maxHandoffDepth);\n try {\n const { reply, result } = await dispatchHandoff({\n descriptor,\n senderAgentId: parentAgentId,\n chainState,\n rawInputJson: input,\n history: { messages: [] }, // v1: history replay deferred\n });\n return JSON.stringify({\n ok: true,\n transferred_to: result.to,\n depth: result.depth,\n reply,\n });\n } catch (err) {\n return JSON.stringify({\n ok: false,\n error: err instanceof Error ? err.name : \"HandoffError\",\n message: err instanceof Error ? err.message : String(err),\n });\n }\n },\n };\n}\n","/**\n * Public `Handoff` class — factory for handoff descriptors (Adoption\n * Roadmap #4; ADRs D214-D229).\n *\n * Usage:\n *\n * import { Agent, Handoff } from \"@theokit/sdk\";\n *\n * const billing = await Agent.create({\n * name: \"billing\",\n * systemPrompt: \"You handle billing questions.\",\n * model: { id: \"openai/gpt-4o-mini\" },\n * apiKey: process.env.OPENROUTER_API_KEY,\n * });\n *\n * const triage = await Agent.create({\n * name: \"triage\",\n * systemPrompt: \"Classify the user's intent and transfer to the right specialist.\",\n * model: { id: \"openai/gpt-4o-mini\" },\n * apiKey: process.env.OPENROUTER_API_KEY,\n * handoffs: [\n * billing, // auto-wrapped as Handoff.create(billing)\n * Handoff.create(supportAgent, { inputFilter: redactCreditCards }),\n * ],\n * });\n *\n * @public\n */\n\nimport { definePlugin, type Plugin, type PluginContext, type SDKAgent } from \"@theokit/sdk\";\nimport type { ZodType } from \"zod\";\nimport type { HandoffDescriptor, HandoffOptions } from \"./types/handoff.js\";\n\n/** Recommended system-prompt prefix for senders (D215 / EC-13). */\nexport const RECOMMENDED_HANDOFF_PROMPT_PREFIX = `\nYou can transfer the conversation to other specialist agents when their\nexpertise matches the user's request. Invoke the appropriate\ntransfer_to_<agent> tool with a short reason. The receiving agent will\ntake over the conversation; do not duplicate their work.\n`.trim();\n\nexport class Handoff {\n private constructor() {}\n\n /**\n * Build a `HandoffDescriptor` for a target agent. Wrap with custom options\n * (filter / inputType / callback / whitelist / etc); pass to\n * `Agent.create({ handoffs: [...] })`.\n *\n * Raw `SDKAgent` instances in `handoffs[]` are auto-wrapped by the runtime\n * — call `Handoff.create()` explicitly only when you need to customize.\n */\n static create<TInput extends ZodType = ZodType>(\n target: SDKAgent,\n options: HandoffOptions<TInput> = {} as HandoffOptions<TInput>,\n ): HandoffDescriptor<TInput> {\n if (target === undefined || target === null) {\n throw new Error(\"Handoff.create: target agent is required\");\n }\n if (typeof target.send !== \"function\") {\n throw new Error(\"Handoff.create: target must be an SDKAgent instance\");\n }\n const resolvedToolName = options.toolName ?? `transfer_to_${slugifyName(target)}`;\n return {\n target,\n options,\n resolvedToolName,\n };\n }\n\n /**\n * Plugin-based wiring (SDK 2.x preferred). Wraps `targets` in synthetic\n * `transfer_to_<receiver>` tools and registers them via `ctx.registerTool`\n * at agent init time.\n *\n * Replaces the legacy `Agent.create({ handoffs: [...] })` option (which is\n * still supported as a transitional convenience while sdk-handoff is\n * installed — the framework lazy-imports the tool-injector at runtime).\n *\n * @example\n * const support = await Agent.create({\n * name: \"support\",\n * plugins: [Handoff.asPlugin({ parentAgentId: \"support\", targets: [billing] })],\n * });\n */\n static asPlugin(opts: AsPluginOptions): Plugin {\n const parent = opts.parentAgentId ?? \"anonymous\";\n const maxDepth = opts.maxHandoffDepth ?? 5;\n const targets = opts.targets;\n return definePlugin({\n name: `handoff-${parent}`,\n version: \"1.0.0\",\n kind: \"general\" as const,\n register(ctx: PluginContext): void {\n if (maxDepth === 0 || targets.length === 0) return;\n // Lazy import — keeps cold path lean if asPlugin is constructed but\n // its register hook is never invoked (e.g., disabled by config).\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n void (async () => {\n const { normalizeHandoffs, buildHandoffTool } = await import(\n \"./internal/tool-injector.js\"\n );\n const normalized = normalizeHandoffs(parent, targets);\n for (const { descriptor } of normalized) {\n ctx.registerTool(buildHandoffTool(parent, descriptor, maxDepth));\n }\n })();\n },\n });\n }\n}\n\n/**\n * Options for `Handoff.asPlugin()`. `parentAgentId` defaults to `\"anonymous\"`;\n * pass the host agent's `name` for correct loop detection in chains.\n */\nexport interface AsPluginOptions {\n readonly targets: ReadonlyArray<SDKAgent | HandoffDescriptor>;\n readonly parentAgentId?: string;\n readonly maxHandoffDepth?: number;\n}\n\nfunction slugifyName(agent: SDKAgent): string {\n const candidate = (agent as unknown as { name?: string }).name ?? agent.agentId ?? \"anonymous\";\n return (\n candidate\n .replace(/^agent-/i, \"\")\n .replace(/[^a-zA-Z0-9_-]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .slice(0, 64) || \"anonymous\"\n );\n}\n\n/**\n * Imperative escape hatch (D225). Useful for tests / programmatic flows\n * that need deterministic handoff without LLM routing.\n *\n * NOTE: this is a STANDALONE helper rather than a method on `SDKAgent`\n * to avoid invasive refactor of the agent class. Behavior is identical\n * to invoking the corresponding synthetic tool would be.\n */\nexport async function handoffTo(\n sender: SDKAgent,\n target: SDKAgent,\n message: string,\n options: HandoffOptions = {},\n): Promise<string> {\n const descriptor = Handoff.create(target, options);\n // Lazy import to avoid loading internal module unless this is called.\n const { dispatchHandoff } = await import(\"./internal/dispatcher.js\");\n const { createChainState } = await import(\"./internal/registry.js\");\n const chainState = createChainState(sender.agentId, 5);\n const { reply } = await dispatchHandoff({\n descriptor,\n senderAgentId: sender.agentId,\n chainState,\n rawInputJson: undefined,\n history: { messages: [] },\n messageOverride: message,\n });\n return reply;\n}\n","/**\n * `@theokit/sdk-handoff` — inter-agent dispatch for `@theokit/sdk`.\n *\n * Extracted from `@theokit/sdk@1.7.0` as part of the SDK 2.0 package split\n * (Phase 4 / T4.1). Integrates with `Agent` via the Plugin protocol —\n * `Handoff.asPlugin({ targets })` returns a `Plugin` consumed by\n * `Agent.create({ plugins: [...] })`.\n *\n * @packageDocumentation\n */\n\nexport {\n type AsPluginOptions,\n Handoff,\n handoffTo,\n RECOMMENDED_HANDOFF_PROMPT_PREFIX,\n} from \"./handoff.js\";\nexport type { HandoffDescriptor, HandoffOptions } from \"./types/handoff.js\";\nexport {\n HandoffLoopError,\n HandoffNameCollisionError,\n HandoffPairLoopError,\n HandoffReceiverDisposedError,\n HandoffSelfReferenceError,\n} from \"./types/handoff.js\";\n"]}
|