@ai-sdk/harness-deepagents 1.0.72 → 1.0.74
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 +15 -0
- package/dist/bridge/index.mjs +185 -20
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/bridge/create-emit-stream-event.ts +4 -0
- package/src/bridge/index.ts +54 -1
- package/src/deepagents-harness.ts +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @ai-sdk/harness-deepagents
|
|
2
2
|
|
|
3
|
+
## 1.0.74
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @ai-sdk/harness@1.0.74
|
|
8
|
+
|
|
9
|
+
## 1.0.73
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 62a9c2a: feat(harness): add support for structured output to `HarnessAgent` via `output` property
|
|
14
|
+
- Updated dependencies [62a9c2a]
|
|
15
|
+
- Updated dependencies [d25cae2]
|
|
16
|
+
- @ai-sdk/harness@1.0.73
|
|
17
|
+
|
|
3
18
|
## 1.0.72
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -57,7 +57,6 @@ async function runBridge(options) {
|
|
|
57
57
|
let currentBoundPort = 0;
|
|
58
58
|
let currentTurnState = "init";
|
|
59
59
|
let activeSocket;
|
|
60
|
-
let activeSocketReadyForLiveEvents = false;
|
|
61
60
|
let isFirstTurn = true;
|
|
62
61
|
let turnAbort;
|
|
63
62
|
let currentUserMessages;
|
|
@@ -141,14 +140,6 @@ async function runBridge(options) {
|
|
|
141
140
|
} catch {
|
|
142
141
|
}
|
|
143
142
|
};
|
|
144
|
-
const sendControl = (msg) => {
|
|
145
|
-
if (activeSocket?.readyState === WS_OPEN) {
|
|
146
|
-
try {
|
|
147
|
-
activeSocket.send(JSON.stringify(msg));
|
|
148
|
-
} catch {
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
143
|
const emit = (event) => {
|
|
153
144
|
const seq = ++seqCounter;
|
|
154
145
|
const line = JSON.stringify({ ...event, seq });
|
|
@@ -156,7 +147,7 @@ async function runBridge(options) {
|
|
|
156
147
|
diskBuffer += `${line}
|
|
157
148
|
`;
|
|
158
149
|
scheduleEventFlush();
|
|
159
|
-
if (
|
|
150
|
+
if (activeSocket?.readyState === WS_OPEN) {
|
|
160
151
|
try {
|
|
161
152
|
activeSocket.send(line);
|
|
162
153
|
} catch {
|
|
@@ -259,8 +250,7 @@ async function runBridge(options) {
|
|
|
259
250
|
const handleInbound = async (msg, ws) => {
|
|
260
251
|
switch (msg.type) {
|
|
261
252
|
case "start": {
|
|
262
|
-
|
|
263
|
-
activeSocketReadyForLiveEvents = true;
|
|
253
|
+
activeSocket = ws;
|
|
264
254
|
const firstTurn = isFirstTurn;
|
|
265
255
|
isFirstTurn = false;
|
|
266
256
|
eventLog = [];
|
|
@@ -340,9 +330,8 @@ async function runBridge(options) {
|
|
|
340
330
|
turnAbort?.abort();
|
|
341
331
|
return;
|
|
342
332
|
case "resume":
|
|
343
|
-
|
|
333
|
+
activeSocket = ws;
|
|
344
334
|
replay(ws, msg.lastSeenEventId);
|
|
345
|
-
activeSocketReadyForLiveEvents = true;
|
|
346
335
|
return;
|
|
347
336
|
case "destroy":
|
|
348
337
|
currentTurnState = "done";
|
|
@@ -354,7 +343,7 @@ async function runBridge(options) {
|
|
|
354
343
|
currentTurnState = "done";
|
|
355
344
|
void writeBridgeMeta("done");
|
|
356
345
|
const data = await onStop?.() ?? {};
|
|
357
|
-
sendControl({ type: "bridge-stop", data });
|
|
346
|
+
sendControl(ws, { type: "bridge-stop", data });
|
|
358
347
|
drainThenExit(ws, 1e3, "stop");
|
|
359
348
|
return;
|
|
360
349
|
}
|
|
@@ -407,9 +396,7 @@ async function runBridge(options) {
|
|
|
407
396
|
ws.close(1008, "unauthorized");
|
|
408
397
|
return;
|
|
409
398
|
}
|
|
410
|
-
|
|
411
|
-
activeSocketReadyForLiveEvents = false;
|
|
412
|
-
sendControl({
|
|
399
|
+
sendControl(ws, {
|
|
413
400
|
type: "bridge-hello",
|
|
414
401
|
state: currentTurnState,
|
|
415
402
|
lastSeq: seqCounter
|
|
@@ -420,7 +407,7 @@ async function runBridge(options) {
|
|
|
420
407
|
const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
|
|
421
408
|
parsed = JSON.parse(text);
|
|
422
409
|
} catch (err) {
|
|
423
|
-
sendControl({
|
|
410
|
+
sendControl(ws, {
|
|
424
411
|
type: "error",
|
|
425
412
|
error: `protocol parse error: ${err.message}`
|
|
426
413
|
});
|
|
@@ -431,7 +418,6 @@ async function runBridge(options) {
|
|
|
431
418
|
ws.on("close", () => {
|
|
432
419
|
if (activeSocket === ws) {
|
|
433
420
|
activeSocket = void 0;
|
|
434
|
-
activeSocketReadyForLiveEvents = false;
|
|
435
421
|
}
|
|
436
422
|
});
|
|
437
423
|
ws.on("error", () => {
|
|
@@ -458,6 +444,14 @@ async function runBridge(options) {
|
|
|
458
444
|
})
|
|
459
445
|
};
|
|
460
446
|
}
|
|
447
|
+
function sendControl(socket, message) {
|
|
448
|
+
if (socket?.readyState === WS_OPEN) {
|
|
449
|
+
try {
|
|
450
|
+
socket.send(JSON.stringify(message));
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
461
455
|
function serialiseError(err) {
|
|
462
456
|
if (err instanceof Error) {
|
|
463
457
|
return { name: err.name, message: err.message, stack: err.stack };
|
|
@@ -569,11 +563,149 @@ import { tool } from "@langchain/core/tools";
|
|
|
569
563
|
|
|
570
564
|
// ../../node_modules/.pnpm/langchain@1.5.3_@langchain+core@1.2.3_@opentelemetry+api@1.9.1_@opentelemetry+exporter-_a0dbd4fc7a66247dcd0e899a4ff3ea34/node_modules/langchain/dist/agents/errors.js
|
|
571
565
|
import { isGraphBubbleUp } from "@langchain/langgraph";
|
|
566
|
+
var StructuredOutputParsingError = class extends Error {
|
|
567
|
+
toolName;
|
|
568
|
+
errors;
|
|
569
|
+
constructor(toolName, errors) {
|
|
570
|
+
super(`Failed to parse structured output for tool '${toolName}':${errors.map((e) => `
|
|
571
|
+
- ${e}`).join("")}.`);
|
|
572
|
+
this.toolName = toolName;
|
|
573
|
+
this.errors = errors;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// ../../node_modules/.pnpm/langchain@1.5.3_@langchain+core@1.2.3_@opentelemetry+api@1.9.1_@opentelemetry+exporter-_a0dbd4fc7a66247dcd0e899a4ff3ea34/node_modules/langchain/dist/agents/model.js
|
|
578
|
+
function isBaseChatModel(model) {
|
|
579
|
+
return "invoke" in model && typeof model.invoke === "function" && "_streamResponseChunks" in model;
|
|
580
|
+
}
|
|
572
581
|
|
|
573
582
|
// ../../node_modules/.pnpm/langchain@1.5.3_@langchain+core@1.2.3_@opentelemetry+api@1.9.1_@opentelemetry+exporter-_a0dbd4fc7a66247dcd0e899a4ff3ea34/node_modules/langchain/dist/agents/responses.js
|
|
574
583
|
import { isInteropZodObject, isInteropZodSchema } from "@langchain/core/utils/types";
|
|
575
584
|
import { Validator, toJsonSchema } from "@langchain/core/utils/json_schema";
|
|
576
585
|
import { isSerializableSchema } from "@langchain/core/utils/standard_schema";
|
|
586
|
+
var PROVIDER_STRATEGY_DEFAULT_STRICT = true;
|
|
587
|
+
var bindingIdentifier = 0;
|
|
588
|
+
var ToolStrategy = class ToolStrategy2 {
|
|
589
|
+
constructor(schema, tool4, options) {
|
|
590
|
+
this.schema = schema;
|
|
591
|
+
this.tool = tool4;
|
|
592
|
+
this.options = options;
|
|
593
|
+
}
|
|
594
|
+
get name() {
|
|
595
|
+
return this.tool.function.name;
|
|
596
|
+
}
|
|
597
|
+
static fromSchema(schema, outputOptions) {
|
|
598
|
+
function getFunctionName(name) {
|
|
599
|
+
return name ?? `extract-${++bindingIdentifier}`;
|
|
600
|
+
}
|
|
601
|
+
if (isSerializableSchema(schema) || isInteropZodSchema(schema)) {
|
|
602
|
+
const asJsonSchema = toJsonSchema(schema);
|
|
603
|
+
return new ToolStrategy2(asJsonSchema, {
|
|
604
|
+
type: "function",
|
|
605
|
+
function: {
|
|
606
|
+
name: getFunctionName(asJsonSchema.title),
|
|
607
|
+
strict: false,
|
|
608
|
+
description: asJsonSchema.description ?? "Tool for extracting structured output from the model's response.",
|
|
609
|
+
parameters: asJsonSchema
|
|
610
|
+
}
|
|
611
|
+
}, outputOptions);
|
|
612
|
+
}
|
|
613
|
+
let functionDefinition;
|
|
614
|
+
if (typeof schema.name === "string" && typeof schema.parameters === "object" && schema.parameters != null) functionDefinition = schema;
|
|
615
|
+
else functionDefinition = {
|
|
616
|
+
name: getFunctionName(schema.title),
|
|
617
|
+
description: schema.description ?? "",
|
|
618
|
+
parameters: schema.schema || schema
|
|
619
|
+
};
|
|
620
|
+
return new ToolStrategy2(toJsonSchema(schema), {
|
|
621
|
+
type: "function",
|
|
622
|
+
function: functionDefinition
|
|
623
|
+
}, outputOptions);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Parse tool arguments according to the schema.
|
|
627
|
+
*
|
|
628
|
+
* @throws {StructuredOutputParsingError} if the response is not valid
|
|
629
|
+
* @param toolArgs - The arguments from the tool call
|
|
630
|
+
* @returns The parsed response according to the schema type
|
|
631
|
+
*/
|
|
632
|
+
parse(toolArgs) {
|
|
633
|
+
const result = new Validator(this.schema).validate(toolArgs);
|
|
634
|
+
if (!result.valid) throw new StructuredOutputParsingError(this.name, result.errors.map((e) => e.error));
|
|
635
|
+
return toolArgs;
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
var ProviderStrategy = class ProviderStrategy2 {
|
|
639
|
+
_schemaType;
|
|
640
|
+
/**
|
|
641
|
+
* The schema to use for the provider strategy
|
|
642
|
+
*/
|
|
643
|
+
schema;
|
|
644
|
+
/**
|
|
645
|
+
* Whether to use strict mode for the provider strategy
|
|
646
|
+
*/
|
|
647
|
+
strict;
|
|
648
|
+
constructor(schemaOrOptions, strict) {
|
|
649
|
+
if ("schema" in schemaOrOptions && typeof schemaOrOptions.schema === "object" && schemaOrOptions.schema !== null && !("type" in schemaOrOptions)) {
|
|
650
|
+
const options = schemaOrOptions;
|
|
651
|
+
this.schema = options.schema;
|
|
652
|
+
this.strict = options.strict ?? PROVIDER_STRATEGY_DEFAULT_STRICT;
|
|
653
|
+
} else {
|
|
654
|
+
this.schema = schemaOrOptions;
|
|
655
|
+
this.strict = strict ?? PROVIDER_STRATEGY_DEFAULT_STRICT;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
static fromSchema(schema, strict) {
|
|
659
|
+
return new ProviderStrategy2(toJsonSchema(schema), strict);
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Parse tool arguments according to the schema. If the response is not valid, return undefined.
|
|
663
|
+
*
|
|
664
|
+
* @param response - The AI message response to parse
|
|
665
|
+
* @returns The parsed response according to the schema type
|
|
666
|
+
*/
|
|
667
|
+
parse(response) {
|
|
668
|
+
let textContent;
|
|
669
|
+
if (typeof response.content === "string") textContent = response.content;
|
|
670
|
+
else if (Array.isArray(response.content)) {
|
|
671
|
+
for (const block of response.content) if (typeof block === "object" && block !== null && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
672
|
+
textContent = block.text;
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
if (!textContent || textContent === "") return;
|
|
677
|
+
try {
|
|
678
|
+
const content = JSON.parse(textContent);
|
|
679
|
+
if (!new Validator(this.schema).validate(content).valid) return;
|
|
680
|
+
return content;
|
|
681
|
+
} catch {
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
function transformResponseFormat(responseFormat, options, model) {
|
|
686
|
+
if (!responseFormat) return [];
|
|
687
|
+
if (typeof responseFormat === "object" && responseFormat !== null && "__responseFormatUndefined" in responseFormat) return [];
|
|
688
|
+
if (Array.isArray(responseFormat)) {
|
|
689
|
+
if (responseFormat.every((item) => item instanceof ToolStrategy || item instanceof ProviderStrategy)) return responseFormat;
|
|
690
|
+
if (responseFormat.every((item) => isSerializableSchema(item))) return responseFormat.map((item) => ToolStrategy.fromSchema(item, options));
|
|
691
|
+
if (responseFormat.every((item) => isInteropZodObject(item))) return responseFormat.map((item) => ToolStrategy.fromSchema(item, options));
|
|
692
|
+
if (responseFormat.every((item) => typeof item === "object" && item !== null && !isInteropZodObject(item) && !isSerializableSchema(item))) return responseFormat.map((item) => ToolStrategy.fromSchema(item, options));
|
|
693
|
+
throw new Error("Invalid response format: list contains mixed types.\nAll items must be either InteropZodObject, Standard Schema, or plain JSON schema objects.");
|
|
694
|
+
}
|
|
695
|
+
if (responseFormat instanceof ToolStrategy || responseFormat instanceof ProviderStrategy) return [responseFormat];
|
|
696
|
+
const useProviderStrategy = hasSupportForJsonSchemaOutput(model);
|
|
697
|
+
if (isSerializableSchema(responseFormat)) return useProviderStrategy ? [ProviderStrategy.fromSchema(responseFormat)] : [ToolStrategy.fromSchema(responseFormat, options)];
|
|
698
|
+
if (isInteropZodObject(responseFormat)) return useProviderStrategy ? [ProviderStrategy.fromSchema(responseFormat)] : [ToolStrategy.fromSchema(responseFormat, options)];
|
|
699
|
+
if (typeof responseFormat === "object" && responseFormat !== null && "properties" in responseFormat) return useProviderStrategy ? [ProviderStrategy.fromSchema(responseFormat)] : [ToolStrategy.fromSchema(responseFormat, options)];
|
|
700
|
+
throw new Error(`Invalid response format: ${String(responseFormat)}`);
|
|
701
|
+
}
|
|
702
|
+
function toolStrategy(responseFormat, options) {
|
|
703
|
+
return transformResponseFormat(responseFormat, options);
|
|
704
|
+
}
|
|
705
|
+
function hasSupportForJsonSchemaOutput(model) {
|
|
706
|
+
if (!model || !isBaseChatModel(model) || !("profile" in model) || typeof model.profile !== "object" || !model.profile) return false;
|
|
707
|
+
return "structuredOutput" in model.profile && model.profile.structuredOutput === true;
|
|
708
|
+
}
|
|
577
709
|
|
|
578
710
|
// ../../node_modules/.pnpm/langchain@1.5.3_@langchain+core@1.2.3_@opentelemetry+api@1.9.1_@opentelemetry+exporter-_a0dbd4fc7a66247dcd0e899a4ff3ea34/node_modules/langchain/dist/agents/middleware/utils.js
|
|
579
711
|
import { AIMessage, ToolMessage } from "@langchain/core/messages";
|
|
@@ -1321,6 +1453,7 @@ function createEmitStreamEvent({
|
|
|
1321
1453
|
configuredModel,
|
|
1322
1454
|
hostToolNames,
|
|
1323
1455
|
mcpToolNames: mcpToolNames2,
|
|
1456
|
+
structuredOutputToolNames = /* @__PURE__ */ new Set(),
|
|
1324
1457
|
emit
|
|
1325
1458
|
}) {
|
|
1326
1459
|
return (event) => {
|
|
@@ -1391,6 +1524,7 @@ function createEmitStreamEvent({
|
|
|
1391
1524
|
}
|
|
1392
1525
|
} else if (kind === "on_tool_start") {
|
|
1393
1526
|
const toolName = event.name ?? "unknown";
|
|
1527
|
+
if (structuredOutputToolNames.has(toolName)) return;
|
|
1394
1528
|
const runId = event.run_id ?? "";
|
|
1395
1529
|
if (!nested && !hostToolNames.has(toolName)) {
|
|
1396
1530
|
const isMcpTool = mcpToolNames2.has(toolName);
|
|
@@ -1415,6 +1549,7 @@ function createEmitStreamEvent({
|
|
|
1415
1549
|
}
|
|
1416
1550
|
} else if (kind === "on_tool_end") {
|
|
1417
1551
|
const toolName = event.name ?? "unknown";
|
|
1552
|
+
if (structuredOutputToolNames.has(toolName)) return;
|
|
1418
1553
|
const runId = event.run_id ?? "";
|
|
1419
1554
|
if (!nested && !hostToolNames.has(toolName)) {
|
|
1420
1555
|
const dynamic = state.dynamicToolRunIds.delete(runId);
|
|
@@ -1732,6 +1867,16 @@ var agent;
|
|
|
1732
1867
|
var currentTurn;
|
|
1733
1868
|
var mcpClient;
|
|
1734
1869
|
var mcpToolNames = /* @__PURE__ */ new Set();
|
|
1870
|
+
var currentResponseFormat;
|
|
1871
|
+
var responseFormatMiddleware = createMiddleware({
|
|
1872
|
+
name: "HarnessResponseFormat",
|
|
1873
|
+
wrapModelCall(request, handler) {
|
|
1874
|
+
return handler({
|
|
1875
|
+
...request,
|
|
1876
|
+
...currentResponseFormat == null ? {} : { responseFormat: currentResponseFormat }
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
});
|
|
1735
1880
|
function buildHostTools(toolSchemas) {
|
|
1736
1881
|
return (toolSchemas ?? []).map(
|
|
1737
1882
|
(schema) => tool3(
|
|
@@ -1759,6 +1904,7 @@ function buildHostTools(toolSchemas) {
|
|
|
1759
1904
|
}
|
|
1760
1905
|
async function runTurn(start, turn) {
|
|
1761
1906
|
currentTurn = turn;
|
|
1907
|
+
currentResponseFormat = start.responseFormat?.type === "json" && start.responseFormat.schema != null ? toolStrategy(start.responseFormat.schema) : void 0;
|
|
1762
1908
|
const emit = (event) => turn.emit(event);
|
|
1763
1909
|
const interruptOn = buildInterruptOn(
|
|
1764
1910
|
start.permissionMode,
|
|
@@ -1785,6 +1931,7 @@ async function runTurn(start, turn) {
|
|
|
1785
1931
|
}
|
|
1786
1932
|
);
|
|
1787
1933
|
const middleware = [
|
|
1934
|
+
responseFormatMiddleware,
|
|
1788
1935
|
...reasoningMiddleware ? [reasoningMiddleware] : [],
|
|
1789
1936
|
...builtinToolFilteringMiddleware ? [builtinToolFilteringMiddleware] : []
|
|
1790
1937
|
];
|
|
@@ -1819,6 +1966,9 @@ async function runTurn(start, turn) {
|
|
|
1819
1966
|
configuredModel: start.model,
|
|
1820
1967
|
hostToolNames,
|
|
1821
1968
|
mcpToolNames,
|
|
1969
|
+
structuredOutputToolNames: new Set(
|
|
1970
|
+
currentResponseFormat?.map((format) => format.name) ?? []
|
|
1971
|
+
),
|
|
1822
1972
|
emit
|
|
1823
1973
|
});
|
|
1824
1974
|
const config = {
|
|
@@ -1842,10 +1992,25 @@ async function runTurn(start, turn) {
|
|
|
1842
1992
|
let resumeInput = {
|
|
1843
1993
|
messages: [{ role: "user", content: start.prompt }]
|
|
1844
1994
|
};
|
|
1995
|
+
let emittedStructuredOutput = false;
|
|
1845
1996
|
while (true) {
|
|
1846
1997
|
const stream = await agent.streamEvents(resumeInput, config);
|
|
1847
1998
|
for await (const event of stream) {
|
|
1848
1999
|
emitStreamEvent(event);
|
|
2000
|
+
const streamEvent = event;
|
|
2001
|
+
const namespace = streamEvent.metadata?.langgraph_checkpoint_ns ?? "";
|
|
2002
|
+
const output = streamEvent.data?.output;
|
|
2003
|
+
if (!emittedStructuredOutput && streamEvent.event === "on_chain_end" && !namespace.includes("|") && output?.structuredResponse !== void 0) {
|
|
2004
|
+
const id = `structured-output-${randomUUID2()}`;
|
|
2005
|
+
emit({ type: "text-start", id });
|
|
2006
|
+
emit({
|
|
2007
|
+
type: "text-delta",
|
|
2008
|
+
id,
|
|
2009
|
+
delta: JSON.stringify(output.structuredResponse)
|
|
2010
|
+
});
|
|
2011
|
+
emit({ type: "text-end", id });
|
|
2012
|
+
emittedStructuredOutput = true;
|
|
2013
|
+
}
|
|
1849
2014
|
}
|
|
1850
2015
|
const actionRequests = await readPendingApprovals();
|
|
1851
2016
|
if (actionRequests.length === 0) break;
|