@the-open-engine/zeroshot 6.39.2 → 6.41.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 (73) hide show
  1. package/README.md +18 -0
  2. package/cli/agent-provider-boundary.js +19 -0
  3. package/cli/agent-provider-boundary.ts +79 -0
  4. package/cli/export-stream.js +94 -0
  5. package/cli/export-stream.ts +118 -0
  6. package/cli/index.js +102 -54
  7. package/cli/json-export.js +2 -38
  8. package/cli/json-export.ts +2 -56
  9. package/cli/semantic-canonical.js +60 -0
  10. package/cli/semantic-canonical.ts +59 -0
  11. package/cli/semantic-contract.js +97 -0
  12. package/cli/semantic-contract.ts +153 -0
  13. package/cli/semantic-events.js +53 -0
  14. package/cli/semantic-events.ts +65 -0
  15. package/cli/semantic-evidence.js +63 -0
  16. package/cli/semantic-evidence.ts +65 -0
  17. package/cli/semantic-export.js +186 -0
  18. package/cli/semantic-export.ts +245 -0
  19. package/cli/semantic-json.js +69 -0
  20. package/cli/semantic-json.ts +69 -0
  21. package/cli/semantic-line-scanner.js +56 -0
  22. package/cli/semantic-line-scanner.ts +54 -0
  23. package/cli/semantic-parser.js +78 -0
  24. package/cli/semantic-parser.ts +95 -0
  25. package/cli/semantic-provider-line.js +91 -0
  26. package/cli/semantic-provider-line.ts +106 -0
  27. package/cli/trace-evidence.js +86 -0
  28. package/cli/trace-evidence.ts +115 -0
  29. package/cli/trace-export.js +120 -0
  30. package/cli/trace-export.ts +164 -0
  31. package/cli/trace-output-record.js +15 -0
  32. package/cli/trace-output-record.ts +18 -0
  33. package/cli/trace-output.js +98 -0
  34. package/cli/trace-output.ts +119 -0
  35. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -1
  36. package/lib/agent-cli-provider/log-prefix.js +8 -6
  37. package/lib/agent-cli-provider/log-prefix.js.map +1 -1
  38. package/lib/cluster/connection-types.d.cts +2 -2
  39. package/lib/cluster/connection-types.d.mts +2 -2
  40. package/lib/cluster/connection-types.d.ts +2 -2
  41. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  42. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  43. package/lib/cluster/generated/protocol.cjs +4 -4
  44. package/lib/cluster/generated/protocol.d.cts +271 -9
  45. package/lib/cluster/generated/protocol.d.mts +271 -9
  46. package/lib/cluster/generated/protocol.d.ts +271 -9
  47. package/lib/cluster/generated/protocol.mjs +4 -4
  48. package/lib/stream-json-parser.js +6 -5
  49. package/npm-shrinkwrap.json +2 -2
  50. package/package.json +9 -18
  51. package/scripts/generate-cluster-types.js +16 -1
  52. package/scripts/node-release-analyzer.js +17 -0
  53. package/scripts/node-release-commits.js +82 -0
  54. package/scripts/release-dry-run.js +1 -1
  55. package/scripts/release-preflight.js +3 -3
  56. package/scripts/semantic-release-notes.js +8 -2
  57. package/src/agent/agent-task-executor.js +36 -40
  58. package/src/agent/output-extraction-json.js +5 -6
  59. package/src/agent/output-extraction-json.ts +4 -7
  60. package/src/agent-cli-provider/log-prefix.ts +12 -7
  61. package/src/claude-task-runner.js +9 -9
  62. package/src/cluster/connection-types.ts +2 -2
  63. package/src/cluster/generated/protocol-schema.ts +1 -1
  64. package/src/cluster/generated/protocol.ts +67 -5
  65. package/src/legacy-lib/stream-json-parser.ts +10 -5
  66. package/src/providers/index.js +1 -19
  67. package/src/task-log-line.d.ts +20 -0
  68. package/src/task-log-line.js +80 -0
  69. package/task-lib/commands/logs.js +5 -8
  70. package/task-lib/sdk-watcher-output.js +31 -0
  71. package/task-lib/sdk-watcher.js +4 -11
  72. package/task-lib/watcher-output-runtime.js +30 -16
  73. package/scripts/rust-distribution.js +0 -924
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const childProcess = require('node:child_process');
4
+ const fs = require('node:fs');
5
+ const { classifyPaths } = require('../.github/ci-path-classifier');
6
+
7
+ const COMMIT_HASH = /^[0-9a-f]{7,64}$/i;
8
+ const RELEASE_NEUTRAL_PATHS = new Set(['.dockerignore', 'AGENTS.md', 'README.md']);
9
+ const RELEASE_NEUTRAL_PREFIXES = ['.github/'];
10
+
11
+ function isReleaseNeutralPath(pathname) {
12
+ if (RELEASE_NEUTRAL_PATHS.has(pathname)) return true;
13
+ for (const prefix of RELEASE_NEUTRAL_PREFIXES) {
14
+ if (pathname.startsWith(prefix)) return true;
15
+ }
16
+ return false;
17
+ }
18
+
19
+ function hasNodeReleasePath(paths) {
20
+ const productPaths = paths.filter((pathname) => !isReleaseNeutralPath(pathname));
21
+ return productPaths.length > 0 && classifyPaths(productPaths).node;
22
+ }
23
+
24
+ function changedPathsForCommit(hash, options = {}) {
25
+ if (!COMMIT_HASH.test(String(hash))) {
26
+ throw new Error(`invalid commit hash for Node release classification: ${hash}`);
27
+ }
28
+ const runGit = options.runGit || childProcess.execFileSync;
29
+ const output = runGit(
30
+ 'git',
31
+ ['diff-tree', '--root', '--no-commit-id', '--name-only', '-r', '-z', hash],
32
+ { cwd: options.cwd || process.cwd(), encoding: 'utf8' }
33
+ );
34
+ return String(output).split('\0').filter(Boolean);
35
+ }
36
+
37
+ function filterNodeCommits(commits, options = {}) {
38
+ const pathsForCommit = options.pathsForCommit || ((hash) => changedPathsForCommit(hash, options));
39
+ return commits.filter((commit) => {
40
+ // Missing identity cannot be classified safely, so retain the commit.
41
+ if (!commit.hash) return true;
42
+ const nodeRelevant = hasNodeReleasePath(pathsForCommit(commit.hash));
43
+ if (!nodeRelevant && options.logger) {
44
+ options.logger.log('Ignoring non-Node commit in Node release: %s', commit.hash);
45
+ }
46
+ return nodeRelevant;
47
+ });
48
+ }
49
+
50
+ function hasNodeReleaseCommit(commits, options = {}) {
51
+ return filterNodeCommits(commits, options).length > 0;
52
+ }
53
+
54
+ function nodeReleaseContext(context, options = {}) {
55
+ return {
56
+ ...context,
57
+ commits: filterNodeCommits(context.commits || [], {
58
+ cwd: context.cwd,
59
+ logger: context.logger,
60
+ pathsForCommit: options.pathsForCommit,
61
+ }),
62
+ };
63
+ }
64
+
65
+ function commitHashesFromStdin() {
66
+ return fs.readFileSync(0, 'utf8').split(/\s+/).filter(Boolean);
67
+ }
68
+
69
+ function main() {
70
+ const commits = commitHashesFromStdin().map((hash) => ({ hash }));
71
+ process.stdout.write(`node=${hasNodeReleaseCommit(commits)}\n`);
72
+ }
73
+
74
+ module.exports = {
75
+ changedPathsForCommit,
76
+ filterNodeCommits,
77
+ hasNodeReleaseCommit,
78
+ hasNodeReleasePath,
79
+ nodeReleaseContext,
80
+ };
81
+
82
+ if (require.main === module) main();
@@ -23,7 +23,7 @@ function pluginName(plugin) {
23
23
 
24
24
  function validationPlugins(releaseConfig) {
25
25
  const allowed = new Set([
26
- '@semantic-release/commit-analyzer',
26
+ './scripts/node-release-analyzer.js',
27
27
  './scripts/semantic-release-notes.js',
28
28
  ]);
29
29
  return releaseConfig.plugins.filter((plugin) => allowed.has(pluginName(plugin)));
@@ -7,7 +7,7 @@ const { REQUIRED_OMP_SDK_SOURCES, validateOmpSdkReleaseAssets } = require('./omp
7
7
 
8
8
  const RELEASE_ORDER = ['patch', 'minor', 'major'];
9
9
  const REQUIRED_PLUGINS = [
10
- '@semantic-release/commit-analyzer',
10
+ './scripts/node-release-analyzer.js',
11
11
  './scripts/semantic-release-notes.js',
12
12
  '@semantic-release/npm',
13
13
  '@semantic-release/github',
@@ -110,12 +110,12 @@ function validateReleaseConfig(packageJson) {
110
110
  }
111
111
 
112
112
  const analyzerPlugin = releaseConfig.plugins.find(
113
- (plugin) => normalizePlugin(plugin) === '@semantic-release/commit-analyzer'
113
+ (plugin) => normalizePlugin(plugin) === './scripts/node-release-analyzer.js'
114
114
  );
115
115
  const analyzerOptions = Array.isArray(analyzerPlugin) ? analyzerPlugin[1] || {} : {};
116
116
  if (Array.isArray(analyzerOptions.releaseRules) && analyzerOptions.releaseRules.length > 0) {
117
117
  throw new Error(
118
- '@semantic-release/commit-analyzer must use the standard conventional release rules'
118
+ './scripts/node-release-analyzer.js must use the standard conventional release rules'
119
119
  );
120
120
  }
121
121
 
@@ -1,5 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
+ const { nodeReleaseContext } = require('./node-release-commits');
3
4
 
4
5
  function curatedNotesPath(cwd, version) {
5
6
  return path.join(cwd, 'docs', 'releases', `v${version}.md`);
@@ -21,13 +22,18 @@ async function conventionalNotes(pluginConfig, context) {
21
22
  return generator.generateNotes(pluginConfig.conventional || {}, context);
22
23
  }
23
24
 
24
- async function generateNotesWithFallback(pluginConfig, context, fallback = conventionalNotes) {
25
+ async function generateNotesWithFallback(
26
+ pluginConfig,
27
+ context,
28
+ fallback = conventionalNotes,
29
+ options = {}
30
+ ) {
25
31
  const version = context.nextRelease?.version;
26
32
  if (!version) throw new Error('nextRelease.version is required to generate release notes');
27
33
 
28
34
  const curated = readCuratedNotes(context.cwd || process.cwd(), version);
29
35
  if (curated) return curated;
30
- const generated = await fallback(pluginConfig, context);
36
+ const generated = await fallback(pluginConfig, nodeReleaseContext(context, options));
31
37
  return generated;
32
38
  }
33
39
 
@@ -18,6 +18,7 @@ const { StringDecoder } = require('string_decoder');
18
18
  const { getNestedExecutionRegistry, TaskExecutionHandle } = require('./task-execution-handle');
19
19
  const os = require('os');
20
20
  const { parseProviderChunk, getProvider } = require('../providers');
21
+ const { decodeTaskLogLine } = require('../task-log-line');
21
22
  const { getTask, getTaskBySpawnOwnershipToken } = require('../../task-lib/store.js');
22
23
  const { OMP_SESSIONLESS_ENV } = require('../../task-lib/omp-storage-root.js');
23
24
  const { loadSettings } = require('../../lib/settings.js');
@@ -1268,9 +1269,10 @@ async function waitForTaskReady(agent, taskId, maxRetries = 10, delayMs = 200) {
1268
1269
 
1269
1270
  const MAX_STATUS_FAILURES = 30;
1270
1271
 
1271
- function createLogFollowState() {
1272
+ function createLogFollowState(taskId = null) {
1272
1273
  const state = {
1273
1274
  controlPlaneOutput: createControlPlaneOutputState(),
1275
+ taskId,
1274
1276
  logFilePath: null,
1275
1277
  lastSize: 0,
1276
1278
  pollInterval: null,
@@ -1471,28 +1473,26 @@ function lookupLogFilePath(ctPath, taskId) {
1471
1473
  }
1472
1474
 
1473
1475
  function parseTimestampedLine(line) {
1474
- let timestamp = Date.now();
1475
- let content = line.replace(/\r$/, '');
1476
- let timestamped = false;
1477
-
1478
- const timestampMatch = content.match(/^\[(\d{13})\](.*)$/);
1479
- if (timestampMatch) {
1480
- timestamp = parseInt(timestampMatch[1], 10);
1481
- content = timestampMatch[2];
1482
- timestamped = true;
1483
- }
1484
-
1485
- return { timestamp, content, timestamped };
1476
+ const decoded = decodeTaskLogLine(line);
1477
+ return {
1478
+ timestamp: decoded.timestamp ?? Date.now(),
1479
+ content: decoded.content,
1480
+ timestamped: decoded.timestamped,
1481
+ providerOutput: decoded.providerOutput,
1482
+ providerStdoutFramed: decoded.channel === 'provider_stdout',
1483
+ };
1486
1484
  }
1487
1485
 
1488
- function shouldSkipLogLine(content, providerName, timestamped) {
1486
+ function shouldSkipLogLine(
1487
+ content,
1488
+ providerName,
1489
+ timestamped,
1490
+ providerOutput,
1491
+ providerStdoutFramed = false
1492
+ ) {
1493
+ if (!providerOutput) return true;
1494
+ if (providerStdoutFramed) return false;
1489
1495
  if (providerName === 'pi') {
1490
- if (
1491
- content.startsWith('[ZEROSHOT][PROVIDER_STDERR] ') ||
1492
- content.startsWith('[ZEROSHOT][FATAL] ')
1493
- ) {
1494
- return true;
1495
- }
1496
1496
  if (timestamped) return false;
1497
1497
  return (
1498
1498
  /^={50}$/.test(content) ||
@@ -1504,7 +1504,6 @@ function shouldSkipLogLine(content, providerName, timestamped) {
1504
1504
  content.startsWith('===') ||
1505
1505
  content.startsWith('Finished:') ||
1506
1506
  content.startsWith('Exit code:') ||
1507
- content.startsWith('[ZEROSHOT][PROVIDER_STDERR] ') ||
1508
1507
  (content.includes('"type":"system"') && content.includes('"subtype":"init"'))
1509
1508
  );
1510
1509
  }
@@ -1522,7 +1521,7 @@ function isValidJsonLine(content) {
1522
1521
  }
1523
1522
  }
1524
1523
 
1525
- function publishAgentOutputRecord(agent, providerName, record) {
1524
+ function publishAgentOutputRecord(agent, providerName, taskId, record) {
1526
1525
  agent._publish({
1527
1526
  topic: 'AGENT_OUTPUT',
1528
1527
  receiver: 'broadcast',
@@ -1536,6 +1535,7 @@ function publishAgentOutputRecord(agent, providerName, record) {
1536
1535
  role: agent.role,
1537
1536
  iteration: agent.iteration,
1538
1537
  provider: providerName,
1538
+ ...(taskId ? { taskId } : {}),
1539
1539
  },
1540
1540
  },
1541
1541
  });
@@ -1545,8 +1545,9 @@ function broadcastAgentLine({ agent, providerName, state, line }) {
1545
1545
  const followerState = ensureControlPlaneOutputState(state);
1546
1546
  if (!line.trim()) return;
1547
1547
 
1548
- const { timestamp, content, timestamped } = parseTimestampedLine(line);
1549
- if (shouldSkipLogLine(content, providerName, timestamped)) {
1548
+ const { timestamp, content, timestamped, providerOutput, providerStdoutFramed } =
1549
+ parseTimestampedLine(line);
1550
+ if (shouldSkipLogLine(content, providerName, timestamped, providerOutput, providerStdoutFramed)) {
1550
1551
  return;
1551
1552
  }
1552
1553
  const controlPlaneContent = redactTerminalFailureForControlPlane(
@@ -1567,14 +1568,14 @@ function broadcastAgentLine({ agent, providerName, state, line }) {
1567
1568
  }
1568
1569
 
1569
1570
  publishLiveControlPlaneRecord(followerState.controlPlaneOutput, record, (item) =>
1570
- publishAgentOutputRecord(agent, providerName, item)
1571
+ publishAgentOutputRecord(agent, providerName, followerState.taskId, item)
1571
1572
  );
1572
1573
  }
1573
1574
 
1574
1575
  function flushAgentOutput(agent, providerName, state) {
1575
1576
  const followerState = ensureControlPlaneOutputState(state);
1576
1577
  flushTerminalControlPlaneOutput(followerState.controlPlaneOutput, (record) =>
1577
- publishAgentOutputRecord(agent, providerName, record)
1578
+ publishAgentOutputRecord(agent, providerName, followerState.taskId, record)
1578
1579
  );
1579
1580
  }
1580
1581
 
@@ -2117,7 +2118,7 @@ function createLogFollower({
2117
2118
  executionHandle = null,
2118
2119
  }) {
2119
2120
  return new Promise((resolve, reject) => {
2120
- const state = createLogFollowState();
2121
+ const state = createLogFollowState(taskId);
2121
2122
  state.skipStructuredResultCheck = skipStructuredResultCheck;
2122
2123
  state.nested = nested;
2123
2124
  state.logFilePath = lookupLogFilePath(ctPath, taskId);
@@ -3022,17 +3023,6 @@ function buildIsolatedLifecycleHandle({
3022
3023
  };
3023
3024
  }
3024
3025
 
3025
- function parseIsolatedLogLine(line) {
3026
- const timestampMatch = line.match(/^\[(\d{13}|\d{4}-\d{2}-\d{2}T[^\]]+)\]\s*(.*)$/);
3027
- const timestamp = timestampMatch
3028
- ? /^\d{13}$/.test(timestampMatch[1])
3029
- ? Number.parseInt(timestampMatch[1], 10)
3030
- : new Date(timestampMatch[1]).getTime()
3031
- : Date.now();
3032
- const content = timestampMatch ? timestampMatch[2] : line;
3033
- return { timestamp, content, timestamped: timestampMatch !== null };
3034
- }
3035
-
3036
3026
  function publishIsolatedOutputRecord(agent, providerName, taskId, record) {
3037
3027
  agent.messageBus.publish({
3038
3028
  cluster_id: agent.cluster.id,
@@ -3052,8 +3042,14 @@ function publishIsolatedOutputRecord(agent, providerName, taskId, record) {
3052
3042
  }
3053
3043
 
3054
3044
  function retainIsolatedLine(state, providerName, line) {
3055
- const { timestamp, content, timestamped } = parseIsolatedLogLine(line);
3056
- if (!content.trim() || shouldSkipLogLine(content, providerName, timestamped)) return null;
3045
+ const { timestamp, content, timestamped, providerOutput, providerStdoutFramed } =
3046
+ parseTimestampedLine(line);
3047
+ if (
3048
+ !content.trim() ||
3049
+ shouldSkipLogLine(content, providerName, timestamped, providerOutput, providerStdoutFramed)
3050
+ ) {
3051
+ return null;
3052
+ }
3057
3053
  const controlPlaneContent = redactTerminalFailureForControlPlane(state, providerName, content);
3058
3054
  return appendControlPlaneRecord(state.controlPlaneOutput, {
3059
3055
  content: controlPlaneContent,
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ const task_log_line_1 = require("../task-log-line");
2
3
  function isObjectRecord(value) {
3
4
  return value !== null && typeof value === 'object' && !Array.isArray(value);
4
5
  }
@@ -14,10 +15,6 @@ if (!isProvidersParserBoundary(rawProviders)) {
14
15
  throw new TypeError('providers module must expose parseProviderChunk');
15
16
  }
16
17
  const { parseProviderChunk } = rawProviders;
17
- function timestampBody(text) {
18
- const match = /^\[(\d{13})\](.*)$/.exec(text);
19
- return match ? (match[2] || '').trimStart() : null;
20
- }
21
18
  function prefixedJsonBody(text) {
22
19
  if (text.startsWith('{') || text.startsWith('['))
23
20
  return null;
@@ -33,8 +30,10 @@ function stripTimestamp(line) {
33
30
  const normalized = line.trim().replace(/\r$/, '');
34
31
  if (!normalized)
35
32
  return '';
36
- const withoutTimestamp = timestampBody(normalized) ?? normalized;
37
- return prefixedJsonBody(withoutTimestamp) ?? withoutTimestamp;
33
+ const decoded = (0, task_log_line_1.decodeTaskLogLine)(normalized);
34
+ if (!decoded.providerOutput)
35
+ return '';
36
+ return prefixedJsonBody(decoded.content) ?? decoded.content;
38
37
  }
39
38
  function parseJsonRecordLine(line) {
40
39
  const content = stripTimestamp(line);
@@ -1,4 +1,5 @@
1
1
  import type { JsonRecord, ProvidersParserBoundary } from './output-extraction-types';
2
+ import { decodeTaskLogLine } from '../task-log-line';
2
3
 
3
4
  function isObjectRecord(value: unknown): value is JsonRecord {
4
5
  return value !== null && typeof value === 'object' && !Array.isArray(value);
@@ -20,11 +21,6 @@ if (!isProvidersParserBoundary(rawProviders)) {
20
21
  }
21
22
  const { parseProviderChunk } = rawProviders;
22
23
 
23
- function timestampBody(text: string): string | null {
24
- const match = /^\[(\d{13})\](.*)$/.exec(text);
25
- return match ? (match[2] || '').trimStart() : null;
26
- }
27
-
28
24
  function prefixedJsonBody(text: string): string | null {
29
25
  if (text.startsWith('{') || text.startsWith('[')) return null;
30
26
  const separatorIndex = text.indexOf('|');
@@ -38,8 +34,9 @@ function stripTimestamp(line: unknown): string {
38
34
  const normalized = line.trim().replace(/\r$/, '');
39
35
  if (!normalized) return '';
40
36
 
41
- const withoutTimestamp = timestampBody(normalized) ?? normalized;
42
- return prefixedJsonBody(withoutTimestamp) ?? withoutTimestamp;
37
+ const decoded = decodeTaskLogLine(normalized);
38
+ if (!decoded.providerOutput) return '';
39
+ return prefixedJsonBody(decoded.content) ?? decoded.content;
43
40
  }
44
41
 
45
42
  function parseJsonRecordLine(line: string): JsonRecord | null {
@@ -1,13 +1,18 @@
1
+ interface TaskLogDecoder {
2
+ decodeTaskLogLine(line: string): { readonly content: string; readonly providerOutput: boolean };
3
+ }
4
+
5
+ // The canonical decoder is maintained JavaScript and remains at the same source path after build.
6
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
7
+ const taskLogDecoder: TaskLogDecoder = require('../../src/task-log-line');
8
+ const { decodeTaskLogLine } = taskLogDecoder;
9
+
1
10
  export function stripTimestampPrefix(line: string): string {
2
- let trimmed = line.trim().replace(/\r$/, '');
11
+ const decoded = decodeTaskLogLine(line.trim());
12
+ if (!decoded.providerOutput) return '';
13
+ let trimmed = decoded.content;
3
14
  if (!trimmed) return '';
4
15
 
5
- const timestampMatch = /^\[(\d{13})\](.*)$/.exec(trimmed);
6
- const timestampRemainder = timestampMatch?.[2];
7
- if (typeof timestampRemainder === 'string') {
8
- trimmed = timestampRemainder.trimStart();
9
- }
10
-
11
16
  if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
12
17
  const pipeMatch = /^[^|]{1,40}\|\s*(.*)$/.exec(trimmed);
13
18
  const afterPipe = pipeMatch?.[1]?.trimStart();
@@ -11,6 +11,7 @@ const TaskRunner = require('./task-runner');
11
11
  const { loadSettings } = require('../lib/settings');
12
12
  const { normalizeProviderName, getDefaultProviderId } = require('../lib/provider-names');
13
13
  const { getProvider } = require('./providers');
14
+ const { decodeTaskLogLine } = require('./task-log-line');
14
15
  const { prependWorktreeToolBinToEnv } = require('./worktree-tooling-env');
15
16
  const { applyDarwinKeychainBoundaryToEnv } = require('./darwin-keychain-boundary');
16
17
  const { getTask, getTaskBySpawnOwnershipToken } = require('../task-lib/store.js');
@@ -556,18 +557,17 @@ class ClaudeTaskRunner extends TaskRunner {
556
557
  const broadcastLine = (line) => {
557
558
  if (!line.trim()) return;
558
559
 
559
- let content = line;
560
- const timestampMatch = line.match(/^\[(\d{13})\](.*)$/);
561
- if (timestampMatch) {
562
- content = timestampMatch[2];
563
- }
560
+ const decoded = decodeTaskLogLine(line);
561
+ if (!decoded.providerOutput) return;
562
+ const content = decoded.content;
564
563
 
565
564
  // Skip non-JSON patterns
566
565
  if (
567
- content.startsWith('===') ||
568
- content.startsWith('Finished:') ||
569
- content.startsWith('Exit code:') ||
570
- (content.includes('"type":"system"') && content.includes('"subtype":"init"'))
566
+ decoded.channel !== 'provider_stdout' &&
567
+ (content.startsWith('===') ||
568
+ content.startsWith('Finished:') ||
569
+ content.startsWith('Exit code:') ||
570
+ (content.includes('"type":"system"') && content.includes('"subtype":"init"')))
571
571
  ) {
572
572
  return;
573
573
  }
@@ -1,12 +1,12 @@
1
1
  import type { FrameRecord } from './frames.js';
2
2
  import type { BoundedQueue } from './queue.js';
3
- import type { ClusterMethod } from './generated/protocol.js';
3
+ import type { ClusterMethod, SubscriptionMethod } from './generated/protocol.js';
4
4
 
5
5
  export interface CallOptions {
6
6
  readonly signal?: AbortSignal;
7
7
  readonly requestTimeoutMs?: number;
8
8
  }
9
- export type SubscriptionKind = 'watch' | 'logs' | 'agent/attach';
9
+ export type SubscriptionKind = SubscriptionMethod;
10
10
  export type SubscriptionRegistration = {
11
11
  readonly id: string;
12
12
  readonly kind: SubscriptionKind;