@bluecopa/harness 0.1.0-snapshot.87 → 0.1.0-snapshot.88

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.
@@ -1,4 +1,4 @@
1
- import { A as ArcLoopConfig, a as AgentMessage, b as ArcEvent, c as ArcRunResult, T as TraceEvent } from '../types-BS_9IiFG.js';
1
+ import { A as ArcLoopConfig, a as AgentMessage, b as ArcEvent, c as ArcRunResult, T as TraceEvent } from '../types-CTO78tn-.js';
2
2
  import 'zod';
3
3
  import 'ai';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
- import { tool, streamText, generateText, generateObject } from 'ai';
2
+ import { tool, streamText, generateText, generateObject, stepCountIs } from 'ai';
3
3
  import { anthropic } from '@ai-sdk/anthropic';
4
4
  import { z } from 'zod';
5
5
  import { readFile } from 'fs/promises';
@@ -883,6 +883,18 @@ function createChannel() {
883
883
  }
884
884
  var DEFAULT_PAGE_THRESHOLD = 4e3;
885
885
  var READ_FULL_RESULT_HARD_CAP = 32e3;
886
+ function extractStepUsage(usage) {
887
+ if (!usage) return void 0;
888
+ const u = {};
889
+ if (usage.inputTokens != null) u.inputTokens = usage.inputTokens;
890
+ if (usage.outputTokens != null) u.outputTokens = usage.outputTokens;
891
+ const inputDetails = usage.inputTokenDetails ?? usage;
892
+ const outputDetails = usage.outputTokenDetails ?? usage;
893
+ if (inputDetails.cacheReadTokens != null) u.cacheReadTokens = inputDetails.cacheReadTokens;
894
+ if (inputDetails.cacheWriteTokens != null) u.cacheWriteTokens = inputDetails.cacheWriteTokens;
895
+ if (outputDetails.reasoningTokens != null) u.reasoningTokens = outputDetails.reasoningTokens;
896
+ return Object.keys(u).length > 0 ? u : void 0;
897
+ }
886
898
  var PROCESS_SYSTEM_PROMPT = [
887
899
  "You are a focused execution thread within a larger agent system.",
888
900
  "Complete the assigned task using the available tools.",
@@ -1261,6 +1273,265 @@ var AgentRunner = class {
1261
1273
  }
1262
1274
  return { messages, output: "max steps reached", steps: config.maxSteps };
1263
1275
  }
1276
+ async *stream(config) {
1277
+ const messages = [
1278
+ ...config.seed ?? [],
1279
+ { role: "user", content: config.prompt }
1280
+ ];
1281
+ const systemContent = config.contextFacts?.length ? config.systemPrompt + "\n\n## Known Facts\n" + config.contextFacts.map((f) => `- ${f}`).join("\n") : config.systemPrompt;
1282
+ const cachedSystem = [{
1283
+ role: "system",
1284
+ content: systemContent
1285
+ }];
1286
+ const pageThreshold = config.resultPageThreshold ?? DEFAULT_PAGE_THRESHOLD;
1287
+ const pagingExcludeSet = new Set(config.pagingExclude ?? []);
1288
+ const effectiveTools = config.resultPager ? {
1289
+ ...config.tools,
1290
+ ReadFullResult: tool({
1291
+ description: "Retrieve the full content of a paged tool result. Use when the summary is insufficient and you need the complete data.",
1292
+ inputSchema: z.object({
1293
+ ref: z.string().describe("The paged result reference from a previous tool output"),
1294
+ lineRange: z.object({
1295
+ start: z.number().int().min(1).describe("Start line (1-indexed, inclusive)"),
1296
+ end: z.number().int().min(1).describe("End line (1-indexed, inclusive)")
1297
+ }).optional().describe("Optional line range to retrieve. Omit for full content.")
1298
+ })
1299
+ })
1300
+ } : config.tools;
1301
+ for (let step = 0; step < config.maxSteps; step++) {
1302
+ config.signal.throwIfAborted();
1303
+ yield { type: "step_start", step: step + 1 };
1304
+ if (config.inbox) {
1305
+ for await (const msg of drainNonBlocking(config.inbox)) {
1306
+ messages.push(msg);
1307
+ }
1308
+ }
1309
+ if (config.maxContextTokens && step > 0) {
1310
+ trimContext(messages, config.maxContextTokens);
1311
+ }
1312
+ const result = streamText({
1313
+ model: (config.createModel ?? anthropic)(config.model),
1314
+ tools: normalizeTools(effectiveTools),
1315
+ toolChoice: resolveToolChoice(config.toolChoice, step),
1316
+ messages: toModelMessages(messages),
1317
+ system: cachedSystem,
1318
+ abortSignal: config.signal,
1319
+ stopWhen: stepCountIs(1)
1320
+ });
1321
+ const toolCalls = [];
1322
+ let finalText = "";
1323
+ for await (const part of result.fullStream) {
1324
+ if (part.type === "text-delta") {
1325
+ finalText += part.text;
1326
+ yield { type: "text_delta", text: part.text };
1327
+ }
1328
+ if (part.type === "tool-call") {
1329
+ const raw = part;
1330
+ toolCalls.push({
1331
+ toolName: raw.toolName,
1332
+ input: raw.args ?? raw.input ?? {},
1333
+ ...raw.toolCallId != null ? { toolCallId: raw.toolCallId } : {},
1334
+ ...raw.providerMetadata || raw.experimental_providerMetadata ? { providerMetadata: raw.providerMetadata ?? raw.experimental_providerMetadata } : {}
1335
+ });
1336
+ }
1337
+ }
1338
+ let usage;
1339
+ try {
1340
+ usage = extractStepUsage(await result.usage);
1341
+ } catch {
1342
+ usage = void 0;
1343
+ }
1344
+ if (toolCalls.length === 0) {
1345
+ const rawText = finalText.trim();
1346
+ if (!rawText && step === 0) {
1347
+ const text2 = "ERROR: LLM returned empty response with no tool calls on first step. This may indicate an API billing issue, authentication error, or rate limit.";
1348
+ messages.push({ role: "assistant", content: text2 });
1349
+ yield usage ? { type: "step_end", step: step + 1, usage } : { type: "step_end", step: step + 1 };
1350
+ return { messages, output: text2, steps: step + 1 };
1351
+ }
1352
+ const text = rawText || "Done.";
1353
+ messages.push({ role: "assistant", content: text });
1354
+ if (config.hookRunner) {
1355
+ const decision = await config.hookRunner.run({
1356
+ event: "RunComplete",
1357
+ metadata: {
1358
+ messages,
1359
+ steps: step + 1,
1360
+ output: text
1361
+ }
1362
+ });
1363
+ if (!decision.allow) {
1364
+ messages.push({
1365
+ role: "user",
1366
+ content: decision.reason ?? "Continue \u2014 a required post-completion step was not performed."
1367
+ });
1368
+ yield usage ? { type: "step_end", step: step + 1, usage } : { type: "step_end", step: step + 1 };
1369
+ continue;
1370
+ }
1371
+ }
1372
+ if (config.outputSchema) {
1373
+ try {
1374
+ const extractionMessages = [
1375
+ ...messages,
1376
+ { role: "user", content: "Extract the structured output from your response above." }
1377
+ ];
1378
+ const structured = await generateObject({
1379
+ model: (config.createModel ?? anthropic)(config.model),
1380
+ schema: config.outputSchema,
1381
+ messages: toModelMessages(extractionMessages),
1382
+ system: config.systemPrompt,
1383
+ abortSignal: config.signal
1384
+ });
1385
+ yield usage ? { type: "step_end", step: step + 1, usage } : { type: "step_end", step: step + 1 };
1386
+ return { messages, output: text, steps: step + 1, structuredOutput: structured.object };
1387
+ } catch {
1388
+ }
1389
+ }
1390
+ yield usage ? { type: "step_end", step: step + 1, usage } : { type: "step_end", step: step + 1 };
1391
+ return { messages, output: text, steps: step + 1 };
1392
+ }
1393
+ const toolCallInfos = toolCalls.map((tc) => {
1394
+ const info = {
1395
+ toolCallId: tc.toolCallId ?? randomUUID(),
1396
+ toolName: tc.toolName,
1397
+ args: tc.input ?? {}
1398
+ };
1399
+ if (tc.providerMetadata) {
1400
+ info.providerMetadata = tc.providerMetadata;
1401
+ }
1402
+ return info;
1403
+ });
1404
+ messages.push({
1405
+ role: "assistant",
1406
+ content: finalText || toolCalls.map((tc) => `${tc.toolName}(${JSON.stringify(tc.input ?? {}).slice(0, 100)})`).join(", "),
1407
+ toolCalls: toolCallInfos
1408
+ });
1409
+ for (let index = 0; index < toolCallInfos.length; index += 1) {
1410
+ const tc = toolCallInfos[index];
1411
+ const action = {
1412
+ type: "tool",
1413
+ name: tc.toolName,
1414
+ args: tc.args,
1415
+ toolCallId: tc.toolCallId
1416
+ };
1417
+ yield { type: "tool_start", name: tc.toolName, args: tc.args, ...tc.toolCallId ? { toolCallId: tc.toolCallId } : {} };
1418
+ if (tc.toolName === "ReadFullResult" && config.resultPager) {
1419
+ const ref = String(tc.args.ref ?? "");
1420
+ const content = await config.resultPager.retrieve(ref);
1421
+ if (!content) {
1422
+ const errorText = "ERROR: Content expired or not found. Use the summary above.";
1423
+ messages.push({
1424
+ role: "tool",
1425
+ content: errorText,
1426
+ toolResults: [{ toolCallId: tc.toolCallId, toolName: tc.toolName, result: errorText, isError: true }]
1427
+ });
1428
+ yield { type: "tool_end", name: tc.toolName, result: { success: false, output: "", error: errorText } };
1429
+ continue;
1430
+ }
1431
+ let output = content;
1432
+ const lr = tc.args.lineRange;
1433
+ if (lr && typeof lr === "object" && "start" in lr && "end" in lr) {
1434
+ const start = Number(lr.start);
1435
+ const end = Number(lr.end);
1436
+ if (Number.isFinite(start) && Number.isFinite(end) && start >= 1 && end >= start) {
1437
+ const lines = content.split("\n");
1438
+ output = lines.slice(start - 1, end).join("\n");
1439
+ }
1440
+ }
1441
+ if (output.length > READ_FULL_RESULT_HARD_CAP) {
1442
+ output = output.slice(0, READ_FULL_RESULT_HARD_CAP) + `
1443
+
1444
+ [Showing first ${READ_FULL_RESULT_HARD_CAP} of ${output.length} chars. Use lineRange for specific sections.]`;
1445
+ }
1446
+ messages.push({
1447
+ role: "tool",
1448
+ content: output,
1449
+ toolResults: [{ toolCallId: tc.toolCallId, toolName: tc.toolName, result: output, isError: false }]
1450
+ });
1451
+ yield { type: "tool_end", name: tc.toolName, result: { success: true, output } };
1452
+ continue;
1453
+ }
1454
+ if (config.allowedToolNames && !config.allowedToolNames.includes(tc.toolName)) {
1455
+ const resultText2 = `ERROR: Tool "${tc.toolName}" is not available in this profile.`;
1456
+ messages.push({
1457
+ role: "tool",
1458
+ content: resultText2,
1459
+ toolResults: [{
1460
+ toolCallId: tc.toolCallId,
1461
+ toolName: tc.toolName,
1462
+ result: resultText2,
1463
+ isError: true
1464
+ }]
1465
+ });
1466
+ yield { type: "tool_end", name: tc.toolName, result: { success: false, output: "", error: resultText2 } };
1467
+ continue;
1468
+ }
1469
+ let toolResult;
1470
+ try {
1471
+ toolResult = await executeTool(action, config.toolProvider, {
1472
+ ...config.executeToolAction != null ? { executeToolAction: config.executeToolAction } : {},
1473
+ ...config.hookRunner != null ? { hookRunner: config.hookRunner } : {},
1474
+ ...config.permissionManager != null ? { permissionManager: config.permissionManager } : {},
1475
+ ...config.askUser != null ? { askUser: config.askUser } : {},
1476
+ ...config.tellUser != null ? { tellUser: config.tellUser } : {},
1477
+ ...config.downloadRawFile != null ? { downloadRawFile: config.downloadRawFile } : {}
1478
+ });
1479
+ } catch (error) {
1480
+ const errorMsg = error instanceof Error ? error.message : String(error);
1481
+ toolResult = {
1482
+ success: false,
1483
+ output: "",
1484
+ error: errorMsg.length > 500 ? errorMsg.slice(0, 500) + "..." : errorMsg
1485
+ };
1486
+ }
1487
+ let resultText = toolResult.success ? toolResult.output : `ERROR: ${toolResult.error ?? "unknown failure"}`;
1488
+ if (config.resultPager && toolResult.success && resultText.length > pageThreshold && !pagingExcludeSet.has(tc.toolName) && tc.toolName !== "ReadFullResult") {
1489
+ try {
1490
+ const paged = await config.resultPager.page(resultText, {
1491
+ toolName: tc.toolName,
1492
+ toolCallId: tc.toolCallId
1493
+ });
1494
+ resultText = [
1495
+ paged.summary,
1496
+ "",
1497
+ `[Full result: ${paged.originalLength} chars \u2014 call ReadFullResult("${paged.ref}") to retrieve]`
1498
+ ].join("\n");
1499
+ } catch {
1500
+ resultText = resultText.slice(0, pageThreshold) + `
1501
+
1502
+ [Truncated \u2014 ${resultText.length} chars total. Storage unavailable.]`;
1503
+ }
1504
+ }
1505
+ if (config.maxToolResultLength && resultText.length > config.maxToolResultLength) {
1506
+ const originalLength = resultText.length;
1507
+ resultText = resultText.slice(0, config.maxToolResultLength) + `
1508
+
1509
+ [Truncated \u2014 ${originalLength} chars total, showing first ${config.maxToolResultLength}.]`;
1510
+ }
1511
+ messages.push({
1512
+ role: "tool",
1513
+ content: resultText,
1514
+ toolResults: [{
1515
+ toolCallId: tc.toolCallId,
1516
+ toolName: tc.toolName,
1517
+ result: resultText,
1518
+ isError: !toolResult.success
1519
+ }]
1520
+ });
1521
+ yield {
1522
+ type: "tool_end",
1523
+ name: tc.toolName,
1524
+ result: {
1525
+ success: toolResult.success,
1526
+ output: toolResult.output,
1527
+ ...toolResult.error != null ? { error: toolResult.error } : {}
1528
+ }
1529
+ };
1530
+ }
1531
+ yield usage ? { type: "step_end", step: step + 1, usage } : { type: "step_end", step: step + 1 };
1532
+ }
1533
+ return { messages, output: "max steps reached", steps: config.maxSteps };
1534
+ }
1264
1535
  };
1265
1536
  function createProcess(request, config) {
1266
1537
  const id = randomUUID();
@@ -1323,39 +1594,86 @@ ${subGuideBlocks}
1323
1594
  }
1324
1595
  }
1325
1596
  const result = await Promise.race([
1326
- runner.run({
1327
- model,
1328
- prompt: request.action,
1329
- tools: config.processTools,
1330
- systemPrompt,
1331
- toolProvider: config.toolProvider,
1332
- maxSteps,
1333
- signal: ac.signal,
1334
- seed,
1335
- inbox: inboxChannel,
1336
- onActivity: (activity) => {
1337
- outbox.push({ type: "activity", activity });
1338
- },
1339
- ...pickDefined(config, [
1340
- "createModel",
1341
- "hookRunner",
1342
- "permissionManager",
1343
- "telemetry",
1344
- "executeToolAction",
1345
- "askUser",
1346
- "tellUser",
1347
- "downloadRawFile",
1348
- "allowedToolNames",
1349
- "outputSchema",
1350
- "toolChoice",
1351
- "resultPager",
1352
- "resultPageThreshold",
1353
- "pagingExclude",
1354
- "maxToolResultLength",
1355
- "contextFacts",
1356
- "maxContextTokens"
1357
- ])
1358
- }),
1597
+ (async () => {
1598
+ const stream = runner.stream({
1599
+ model,
1600
+ prompt: request.action,
1601
+ tools: config.processTools,
1602
+ systemPrompt,
1603
+ toolProvider: config.toolProvider,
1604
+ maxSteps,
1605
+ signal: ac.signal,
1606
+ seed,
1607
+ inbox: inboxChannel,
1608
+ ...pickDefined(config, [
1609
+ "createModel",
1610
+ "hookRunner",
1611
+ "permissionManager",
1612
+ "telemetry",
1613
+ "executeToolAction",
1614
+ "askUser",
1615
+ "tellUser",
1616
+ "downloadRawFile",
1617
+ "allowedToolNames",
1618
+ "outputSchema",
1619
+ "toolChoice",
1620
+ "resultPager",
1621
+ "resultPageThreshold",
1622
+ "pagingExclude",
1623
+ "maxToolResultLength",
1624
+ "contextFacts",
1625
+ "maxContextTokens"
1626
+ ])
1627
+ });
1628
+ while (true) {
1629
+ const next = await stream.next();
1630
+ if (next.done) {
1631
+ return next.value;
1632
+ }
1633
+ const event = next.value;
1634
+ if (event.type === "text_delta") {
1635
+ outbox.push({ type: "text_delta", text: event.text });
1636
+ continue;
1637
+ }
1638
+ if (event.type === "step_start") {
1639
+ outbox.push({ type: "step_start", step: event.step });
1640
+ continue;
1641
+ }
1642
+ if (event.type === "step_end") {
1643
+ outbox.push({
1644
+ type: "step_end",
1645
+ step: event.step,
1646
+ ...event.usage ? { usage: event.usage } : {}
1647
+ });
1648
+ continue;
1649
+ }
1650
+ if (event.type === "tool_start") {
1651
+ outbox.push({
1652
+ type: "activity",
1653
+ activity: {
1654
+ type: "tool_start",
1655
+ name: event.name,
1656
+ args: event.args,
1657
+ ts: Date.now()
1658
+ }
1659
+ });
1660
+ continue;
1661
+ }
1662
+ if (event.type === "tool_end") {
1663
+ outbox.push({
1664
+ type: "activity",
1665
+ activity: {
1666
+ type: "tool_end",
1667
+ name: event.name,
1668
+ ok: event.result.success,
1669
+ ms: 0,
1670
+ preview: String(event.result.output ?? "").slice(0, 200),
1671
+ ts: Date.now()
1672
+ }
1673
+ });
1674
+ }
1675
+ }
1676
+ })(),
1359
1677
  timeoutPromise(config.processTimeout)
1360
1678
  ]);
1361
1679
  const durationMs = Date.now() - startTime;
@@ -2302,16 +2620,12 @@ ${a.content}`).join("\n\n");
2302
2620
  );
2303
2621
  if (active.length > 0) {
2304
2622
  const deadline = Date.now() + (this.config.processTimeout ?? 12e4);
2305
- await new Promise((resolve) => {
2306
- const check = () => {
2307
- if (active.every((p) => p.status !== "running" && p.status !== "pending") || Date.now() > deadline) {
2308
- resolve();
2309
- } else {
2310
- setTimeout(check, 10);
2311
- }
2312
- };
2313
- check();
2314
- });
2623
+ while (active.some((p) => p.status === "running" || p.status === "pending") && Date.now() <= deadline) {
2624
+ if (this.pendingEvents.length === 0) {
2625
+ await this.waitForPendingEvent(signal, 400);
2626
+ }
2627
+ yield* this.drainPendingEvents();
2628
+ }
2315
2629
  }
2316
2630
  const completionMessages = [];
2317
2631
  for (const [id, proc] of this.processes) {
@@ -2460,6 +2774,28 @@ ${hints.join("\n")}`
2460
2774
  id: proc.id,
2461
2775
  activity: event.activity
2462
2776
  });
2777
+ } else if (event.type === "text_delta") {
2778
+ this.traceProcessRunning(proc.id);
2779
+ this.enqueuePendingEvent({
2780
+ type: "process_text_delta",
2781
+ id: proc.id,
2782
+ text: event.text
2783
+ });
2784
+ } else if (event.type === "step_start") {
2785
+ this.traceProcessRunning(proc.id);
2786
+ this.enqueuePendingEvent({
2787
+ type: "process_step_start",
2788
+ id: proc.id,
2789
+ step: event.step
2790
+ });
2791
+ } else if (event.type === "step_end") {
2792
+ this.traceProcessRunning(proc.id);
2793
+ this.enqueuePendingEvent({
2794
+ type: "process_step_end",
2795
+ id: proc.id,
2796
+ step: event.step,
2797
+ ...event.usage ? { usage: event.usage } : {}
2798
+ });
2463
2799
  } else if (event.type === "done") {
2464
2800
  this.traceProcessRunning(proc.id);
2465
2801
  this.trace({ type: "process_transition", id: proc.id, from: "running", to: "completed" });