@the-open-engine/zeroshot 6.39.2 → 6.40.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 +12 -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/stream-json-parser.js +6 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +6 -4
- 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/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
|
@@ -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
|
}
|
|
@@ -16,9 +16,16 @@ interface ProviderFacade {
|
|
|
16
16
|
listProviders(): string[];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
interface TaskLogDecoder {
|
|
20
|
+
decodeTaskLogLine(line: string): { content: string; providerOutput: boolean };
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
// The runtime facade is maintained JavaScript; keep its original emitted require path.
|
|
20
24
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
21
25
|
const { getProvider, listProviders }: ProviderFacade = require('../src/providers');
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
27
|
+
const taskLogDecoder: TaskLogDecoder = require('../src/task-log-line');
|
|
28
|
+
const { decodeTaskLogLine } = taskLogDecoder;
|
|
22
29
|
|
|
23
30
|
function createProviderParsers(): ProviderParser[] {
|
|
24
31
|
return listProviders().map((name) => getProvider(name));
|
|
@@ -26,11 +33,9 @@ function createProviderParsers(): ProviderParser[] {
|
|
|
26
33
|
|
|
27
34
|
function stripTimestampPrefix(line: unknown): string {
|
|
28
35
|
if (!line || typeof line !== 'string') return '';
|
|
29
|
-
|
|
30
|
-
if (!
|
|
31
|
-
|
|
32
|
-
const tsMatch = /^\[(\d{13})\](.*)$/.exec(trimmed);
|
|
33
|
-
if (tsMatch) trimmed = (tsMatch[2] || '').trimStart();
|
|
36
|
+
const decoded = decodeTaskLogLine(line.trim());
|
|
37
|
+
if (!decoded.providerOutput) return '';
|
|
38
|
+
const trimmed = decoded.content;
|
|
34
39
|
|
|
35
40
|
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
36
41
|
const pipeMatch = /^[^|]{1,40}\|\s*(.*)$/.exec(trimmed);
|
package/src/providers/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
} = require('../../lib/provider-names');
|
|
7
7
|
const { commandExists, getCommandPath } = require('../../lib/provider-detection');
|
|
8
8
|
const helper = require('../../lib/agent-cli-provider');
|
|
9
|
+
const { stripTimestampPrefix } = require('../../lib/agent-cli-provider/log-prefix');
|
|
9
10
|
|
|
10
11
|
const warned = new Set();
|
|
11
12
|
|
|
@@ -218,25 +219,6 @@ function listProviders() {
|
|
|
218
219
|
return helper.listProviderAdapters();
|
|
219
220
|
}
|
|
220
221
|
|
|
221
|
-
function stripTimestampPrefix(line) {
|
|
222
|
-
if (!line || typeof line !== 'string') return '';
|
|
223
|
-
let trimmed = line.trim().replace(/\r$/, '');
|
|
224
|
-
if (!trimmed) return '';
|
|
225
|
-
|
|
226
|
-
const tsMatch = trimmed.match(/^\[(\d{13})\](.*)$/);
|
|
227
|
-
if (tsMatch) trimmed = (tsMatch[2] || '').trimStart();
|
|
228
|
-
|
|
229
|
-
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
|
|
230
|
-
const pipeMatch = trimmed.match(/^[^|]{1,40}\|\s*(.*)$/);
|
|
231
|
-
if (pipeMatch) {
|
|
232
|
-
const afterPipe = (pipeMatch[1] || '').trimStart();
|
|
233
|
-
if (afterPipe.startsWith('{') || afterPipe.startsWith('[')) return afterPipe;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return trimmed;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
222
|
function collectEvents(events, event) {
|
|
241
223
|
if (!event) return;
|
|
242
224
|
if (Array.isArray(event)) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const TASK_LOG_V2_MARKER: '[ZEROSHOT][LOG_FORMAT] channel-framed-v2';
|
|
2
|
+
export const TASK_LOG_STDOUT_PREFIX: '[ZEROSHOT][PROVIDER_STDOUT] ';
|
|
3
|
+
export const TASK_LOG_STDERR_PREFIX: '[ZEROSHOT][PROVIDER_STDERR] ';
|
|
4
|
+
|
|
5
|
+
export type TaskLogChannel = 'provider_stdout' | 'provider_stderr' | 'control' | 'legacy';
|
|
6
|
+
export type TaskLogFormat = 'stderr-tagged-v1' | 'channel-framed-v2' | null;
|
|
7
|
+
|
|
8
|
+
export interface DecodedTaskLogLine {
|
|
9
|
+
readonly channel: TaskLogChannel;
|
|
10
|
+
readonly content: string;
|
|
11
|
+
readonly format: TaskLogFormat;
|
|
12
|
+
readonly providerOutput: boolean;
|
|
13
|
+
readonly timestamp: number | null;
|
|
14
|
+
readonly timestamped: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function decodeTaskLogLine(line: string): DecodedTaskLogLine;
|
|
18
|
+
export function formatTaskLogMarker(timestamp: number): string;
|
|
19
|
+
export function formatTaskLogStderr(timestamp: number, content: string): string;
|
|
20
|
+
export function formatTaskLogStdout(timestamp: number, content: string): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const TASK_LOG_V2_MARKER = '[ZEROSHOT][LOG_FORMAT] channel-framed-v2';
|
|
4
|
+
const TASK_LOG_STDOUT_PREFIX = '[ZEROSHOT][PROVIDER_STDOUT] ';
|
|
5
|
+
const TASK_LOG_STDERR_PREFIX = '[ZEROSHOT][PROVIDER_STDERR] ';
|
|
6
|
+
const TASK_LOG_V1_MARKER = '[ZEROSHOT][LOG_FORMAT] stderr-tagged-v1';
|
|
7
|
+
const CONTROL_LINE =
|
|
8
|
+
/^\[(?:ATTACH|CLEANUP|CRASH|DETACH|ERROR|OMP-OWNERSHIP|SDK-DIAGNOSTIC|SYSTEM)\](?:\s|$)/;
|
|
9
|
+
|
|
10
|
+
function timestampParts(line) {
|
|
11
|
+
const normalized = line.replace(/\r$/, '');
|
|
12
|
+
const match = /^\[(\d{13}|\d{4}-\d{2}-\d{2}T[^\]]+)\](.*)$/.exec(normalized);
|
|
13
|
+
if (!match) return { content: normalized, timestamp: null };
|
|
14
|
+
const rawTimestamp = match[1];
|
|
15
|
+
const epochTimestamp = /^\d{13}$/.test(rawTimestamp);
|
|
16
|
+
const timestamp = epochTimestamp
|
|
17
|
+
? Number.parseInt(rawTimestamp, 10)
|
|
18
|
+
: new Date(rawTimestamp).getTime();
|
|
19
|
+
return {
|
|
20
|
+
content: match[2].trimStart(),
|
|
21
|
+
timestamp: Number.isFinite(timestamp) ? timestamp : null,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function decodeTaskLogLine(line) {
|
|
26
|
+
const { content, timestamp } = timestampParts(line);
|
|
27
|
+
const common = { timestamp, timestamped: timestamp !== null };
|
|
28
|
+
if (content.startsWith(TASK_LOG_STDOUT_PREFIX)) {
|
|
29
|
+
return {
|
|
30
|
+
...common,
|
|
31
|
+
channel: 'provider_stdout',
|
|
32
|
+
content: content.slice(TASK_LOG_STDOUT_PREFIX.length),
|
|
33
|
+
format: null,
|
|
34
|
+
providerOutput: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (content.startsWith(TASK_LOG_STDERR_PREFIX)) {
|
|
38
|
+
return {
|
|
39
|
+
...common,
|
|
40
|
+
channel: 'provider_stderr',
|
|
41
|
+
content: content.slice(TASK_LOG_STDERR_PREFIX.length),
|
|
42
|
+
format: null,
|
|
43
|
+
providerOutput: false,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
let format = null;
|
|
47
|
+
if (content === TASK_LOG_V2_MARKER) format = 'channel-framed-v2';
|
|
48
|
+
else if (content === TASK_LOG_V1_MARKER) format = 'stderr-tagged-v1';
|
|
49
|
+
const control =
|
|
50
|
+
format !== null || content.startsWith('[ZEROSHOT][FATAL] ') || CONTROL_LINE.test(content);
|
|
51
|
+
return {
|
|
52
|
+
...common,
|
|
53
|
+
channel: control ? 'control' : 'legacy',
|
|
54
|
+
content,
|
|
55
|
+
format,
|
|
56
|
+
providerOutput: !control,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function formatTaskLogMarker(timestamp) {
|
|
61
|
+
return `[${timestamp}]${TASK_LOG_V2_MARKER}\n`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function formatTaskLogStdout(timestamp, content) {
|
|
65
|
+
return `[${timestamp}]${TASK_LOG_STDOUT_PREFIX}${content}\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function formatTaskLogStderr(timestamp, content) {
|
|
69
|
+
return `[${timestamp}]${TASK_LOG_STDERR_PREFIX}${content}\n`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
TASK_LOG_STDERR_PREFIX,
|
|
74
|
+
TASK_LOG_STDOUT_PREFIX,
|
|
75
|
+
TASK_LOG_V2_MARKER,
|
|
76
|
+
decodeTaskLogLine,
|
|
77
|
+
formatTaskLogMarker,
|
|
78
|
+
formatTaskLogStderr,
|
|
79
|
+
formatTaskLogStdout,
|
|
80
|
+
};
|
|
@@ -7,6 +7,7 @@ import { createRequire } from 'module';
|
|
|
7
7
|
// Import cluster's stream parser (shared between task and cluster)
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
9
9
|
const { parseChunk } = require('../../lib/stream-json-parser');
|
|
10
|
+
const { decodeTaskLogLine } = require('../../src/task-log-line');
|
|
10
11
|
|
|
11
12
|
// Tool icons for different tool types
|
|
12
13
|
const TOOL_ICONS = {
|
|
@@ -271,16 +272,12 @@ function handleMulti(event) {
|
|
|
271
272
|
}
|
|
272
273
|
|
|
273
274
|
// Parse a raw log line (may have timestamp prefix)
|
|
274
|
-
function parseLogLine(line) {
|
|
275
|
-
|
|
275
|
+
export function parseLogLine(line) {
|
|
276
|
+
const decoded = decodeTaskLogLine(line);
|
|
277
|
+
if (!decoded.providerOutput) return [];
|
|
278
|
+
const trimmed = decoded.content.trim();
|
|
276
279
|
if (!trimmed) return [];
|
|
277
280
|
|
|
278
|
-
// Strip timestamp prefix if present: [1234567890]{...} -> {...}
|
|
279
|
-
const timestampMatch = trimmed.match(/^\[\d+\](.*)$/);
|
|
280
|
-
if (timestampMatch) {
|
|
281
|
-
trimmed = timestampMatch[1];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
281
|
// Non-JSON lines output as-is
|
|
285
282
|
if (!trimmed.startsWith('{')) {
|
|
286
283
|
return [{ type: 'text', text: trimmed + '\n' }];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const {
|
|
5
|
+
formatTaskLogMarker,
|
|
6
|
+
formatTaskLogStderr,
|
|
7
|
+
formatTaskLogStdout,
|
|
8
|
+
} = require('../src/task-log-line.js');
|
|
9
|
+
|
|
10
|
+
export function markSdkTaskLog(log, timestamp = Date.now()) {
|
|
11
|
+
log(formatTaskLogMarker(timestamp));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function normalizedTerminalEvent(terminal) {
|
|
15
|
+
if (terminal.type === 'result') return terminal.event;
|
|
16
|
+
return {
|
|
17
|
+
type: 'result',
|
|
18
|
+
success: false,
|
|
19
|
+
error: { ...terminal.frame.error },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function logSdkTerminal(log, result, now = Date.now) {
|
|
24
|
+
for (const frame of result.progress) {
|
|
25
|
+
log(formatTaskLogStdout(now(), JSON.stringify(frame)));
|
|
26
|
+
}
|
|
27
|
+
log(formatTaskLogStdout(now(), JSON.stringify(normalizedTerminalEvent(result.terminal))));
|
|
28
|
+
for (const line of (result.diagnosticStderr || '').replace(/\r/g, '').split('\n')) {
|
|
29
|
+
if (line) log(formatTaskLogStderr(now(), line));
|
|
30
|
+
}
|
|
31
|
+
}
|
package/task-lib/sdk-watcher.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { appendFileSync } from 'fs';
|
|
8
8
|
import { getTask, updateTask } from './store.js';
|
|
9
9
|
import { createCommandSpecCleanup } from './command-spec-cleanup.js';
|
|
10
|
+
import { logSdkTerminal, markSdkTaskLog } from './sdk-watcher-output.js';
|
|
10
11
|
import {
|
|
11
12
|
completePendingWatcherCancellation,
|
|
12
13
|
completeWatcherFailure,
|
|
@@ -72,16 +73,6 @@ function completionFor(result) {
|
|
|
72
73
|
};
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
function logTerminal(result) {
|
|
76
|
-
for (const frame of result.progress) log(`[${Date.now()}]${JSON.stringify(frame)}\n`);
|
|
77
|
-
const terminal =
|
|
78
|
-
result.terminal.type === 'result' ? result.terminal.event : result.terminal.frame;
|
|
79
|
-
log(`[${Date.now()}]${JSON.stringify(terminal)}\n`);
|
|
80
|
-
if (result.diagnosticStderr) {
|
|
81
|
-
log(`[${Date.now()}][SDK-DIAGNOSTIC] ${result.diagnosticStderr}\n`);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
76
|
async function terminateOwnedProviderBoundary() {
|
|
86
77
|
if (terminalResult) return terminalResult.cleanupAttestation.clean === true;
|
|
87
78
|
if (!running) return true;
|
|
@@ -116,6 +107,8 @@ process.on('unhandledRejection', (reason) => {
|
|
|
116
107
|
void crashWithError(reason, 'unhandledRejection');
|
|
117
108
|
});
|
|
118
109
|
|
|
110
|
+
markSdkTaskLog(log);
|
|
111
|
+
|
|
119
112
|
if (
|
|
120
113
|
await completePendingWatcherCancellation({
|
|
121
114
|
taskId,
|
|
@@ -139,7 +132,7 @@ try {
|
|
|
139
132
|
if (getTask(taskId)?.cancelRequested) running.cancel();
|
|
140
133
|
|
|
141
134
|
terminalResult = await running.result;
|
|
142
|
-
|
|
135
|
+
logSdkTerminal(log, terminalResult);
|
|
143
136
|
await completeWatcherTask({
|
|
144
137
|
taskId,
|
|
145
138
|
completion: completionFor(terminalResult),
|