@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.
- package/README.md +18 -0
- package/cli/agent-provider-boundary.js +19 -0
- package/cli/agent-provider-boundary.ts +79 -0
- package/cli/export-stream.js +94 -0
- package/cli/export-stream.ts +118 -0
- package/cli/index.js +102 -54
- package/cli/json-export.js +2 -38
- package/cli/json-export.ts +2 -56
- package/cli/semantic-canonical.js +60 -0
- package/cli/semantic-canonical.ts +59 -0
- package/cli/semantic-contract.js +97 -0
- package/cli/semantic-contract.ts +153 -0
- package/cli/semantic-events.js +53 -0
- package/cli/semantic-events.ts +65 -0
- package/cli/semantic-evidence.js +63 -0
- package/cli/semantic-evidence.ts +65 -0
- package/cli/semantic-export.js +186 -0
- package/cli/semantic-export.ts +245 -0
- package/cli/semantic-json.js +69 -0
- package/cli/semantic-json.ts +69 -0
- package/cli/semantic-line-scanner.js +56 -0
- package/cli/semantic-line-scanner.ts +54 -0
- package/cli/semantic-parser.js +78 -0
- package/cli/semantic-parser.ts +95 -0
- package/cli/semantic-provider-line.js +91 -0
- package/cli/semantic-provider-line.ts +106 -0
- package/cli/trace-evidence.js +86 -0
- package/cli/trace-evidence.ts +115 -0
- package/cli/trace-export.js +120 -0
- package/cli/trace-export.ts +164 -0
- package/cli/trace-output-record.js +15 -0
- package/cli/trace-output-record.ts +18 -0
- package/cli/trace-output.js +98 -0
- package/cli/trace-output.ts +119 -0
- package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -1
- package/lib/agent-cli-provider/log-prefix.js +8 -6
- package/lib/agent-cli-provider/log-prefix.js.map +1 -1
- package/lib/cluster/connection-types.d.cts +2 -2
- package/lib/cluster/connection-types.d.mts +2 -2
- package/lib/cluster/connection-types.d.ts +2 -2
- package/lib/cluster/generated/protocol-schema.cjs +1 -1
- package/lib/cluster/generated/protocol-schema.mjs +1 -1
- package/lib/cluster/generated/protocol.cjs +4 -4
- package/lib/cluster/generated/protocol.d.cts +271 -9
- package/lib/cluster/generated/protocol.d.mts +271 -9
- package/lib/cluster/generated/protocol.d.ts +271 -9
- package/lib/cluster/generated/protocol.mjs +4 -4
- package/lib/stream-json-parser.js +6 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +9 -18
- package/scripts/generate-cluster-types.js +16 -1
- package/scripts/node-release-analyzer.js +17 -0
- package/scripts/node-release-commits.js +82 -0
- package/scripts/release-dry-run.js +1 -1
- package/scripts/release-preflight.js +3 -3
- package/scripts/semantic-release-notes.js +8 -2
- package/src/agent/agent-task-executor.js +36 -40
- package/src/agent/output-extraction-json.js +5 -6
- package/src/agent/output-extraction-json.ts +4 -7
- package/src/agent-cli-provider/log-prefix.ts +12 -7
- package/src/claude-task-runner.js +9 -9
- package/src/cluster/connection-types.ts +2 -2
- package/src/cluster/generated/protocol-schema.ts +1 -1
- package/src/cluster/generated/protocol.ts +67 -5
- package/src/legacy-lib/stream-json-parser.ts +10 -5
- package/src/providers/index.js +1 -19
- package/src/task-log-line.d.ts +20 -0
- package/src/task-log-line.js +80 -0
- package/task-lib/commands/logs.js +5 -8
- package/task-lib/sdk-watcher-output.js +31 -0
- package/task-lib/sdk-watcher.js +4 -11
- package/task-lib/watcher-output-runtime.js +30 -16
- 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
|
-
'
|
|
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
|
-
'
|
|
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) === '
|
|
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
|
-
'
|
|
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(
|
|
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
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
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(
|
|
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 } =
|
|
1549
|
-
|
|
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 } =
|
|
3056
|
-
|
|
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
|
|
37
|
-
|
|
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
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
-
|
|
568
|
-
content.startsWith('
|
|
569
|
-
|
|
570
|
-
|
|
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 =
|
|
9
|
+
export type SubscriptionKind = SubscriptionMethod;
|
|
10
10
|
export type SubscriptionRegistration = {
|
|
11
11
|
readonly id: string;
|
|
12
12
|
readonly kind: SubscriptionKind;
|