@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,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canonicalEvent = canonicalEvent;
|
|
4
|
+
const semantic_json_1 = require("./semantic-json");
|
|
5
|
+
const RESULT_OPTIONAL_KEYS = [
|
|
6
|
+
'result',
|
|
7
|
+
'error',
|
|
8
|
+
'cost',
|
|
9
|
+
'duration',
|
|
10
|
+
'inputTokens',
|
|
11
|
+
'outputTokens',
|
|
12
|
+
'cacheReadInputTokens',
|
|
13
|
+
'cacheCreationInputTokens',
|
|
14
|
+
'modelUsage',
|
|
15
|
+
'requests',
|
|
16
|
+
'usageSource',
|
|
17
|
+
'usageCompleteness',
|
|
18
|
+
'invocation',
|
|
19
|
+
'ompSdk',
|
|
20
|
+
];
|
|
21
|
+
function canonicalToolEvent(event) {
|
|
22
|
+
if (event.type === 'tool_call') {
|
|
23
|
+
return {
|
|
24
|
+
type: 'tool_call',
|
|
25
|
+
toolName: event.toolName ?? null,
|
|
26
|
+
toolId: event.toolId ?? null,
|
|
27
|
+
input: (0, semantic_json_1.normalizeUnknown)(event.input),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (event.type !== 'tool_result')
|
|
31
|
+
return null;
|
|
32
|
+
return {
|
|
33
|
+
type: 'tool_result',
|
|
34
|
+
toolId: event.toolId ?? null,
|
|
35
|
+
content: (0, semantic_json_1.normalizeUnknown)(event.content),
|
|
36
|
+
isError: (0, semantic_json_1.normalizeUnknown)(event.isError),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function canonicalResult(event) {
|
|
40
|
+
if (event.type !== 'result' || typeof event.success !== 'boolean') {
|
|
41
|
+
throw new semantic_json_1.SemanticProjectionError('event_invalid');
|
|
42
|
+
}
|
|
43
|
+
const normalized = { type: 'result', success: event.success };
|
|
44
|
+
for (const key of RESULT_OPTIONAL_KEYS) {
|
|
45
|
+
if (event[key] !== undefined)
|
|
46
|
+
normalized[key] = (0, semantic_json_1.normalizeUnknown)(event[key]);
|
|
47
|
+
}
|
|
48
|
+
return normalized;
|
|
49
|
+
}
|
|
50
|
+
function canonicalEvent(event) {
|
|
51
|
+
if (!event || typeof event !== 'object' || typeof event.type !== 'string') {
|
|
52
|
+
throw new semantic_json_1.SemanticProjectionError('event_invalid');
|
|
53
|
+
}
|
|
54
|
+
if (event.type === 'text' || event.type === 'thinking') {
|
|
55
|
+
if (typeof event.text !== 'string')
|
|
56
|
+
throw new semantic_json_1.SemanticProjectionError('event_invalid');
|
|
57
|
+
return { type: event.type, text: (0, semantic_json_1.normalizeUnknown)(event.text) };
|
|
58
|
+
}
|
|
59
|
+
return canonicalToolEvent(event) ?? canonicalResult(event);
|
|
60
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { OutputEvent } from './agent-provider-boundary';
|
|
2
|
+
import { normalizeUnknown, SemanticProjectionError } from './semantic-json';
|
|
3
|
+
|
|
4
|
+
const RESULT_OPTIONAL_KEYS = [
|
|
5
|
+
'result',
|
|
6
|
+
'error',
|
|
7
|
+
'cost',
|
|
8
|
+
'duration',
|
|
9
|
+
'inputTokens',
|
|
10
|
+
'outputTokens',
|
|
11
|
+
'cacheReadInputTokens',
|
|
12
|
+
'cacheCreationInputTokens',
|
|
13
|
+
'modelUsage',
|
|
14
|
+
'requests',
|
|
15
|
+
'usageSource',
|
|
16
|
+
'usageCompleteness',
|
|
17
|
+
'invocation',
|
|
18
|
+
'ompSdk',
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
function canonicalToolEvent(event: OutputEvent): Record<string, unknown> | null {
|
|
22
|
+
if (event.type === 'tool_call') {
|
|
23
|
+
return {
|
|
24
|
+
type: 'tool_call',
|
|
25
|
+
toolName: event.toolName ?? null,
|
|
26
|
+
toolId: event.toolId ?? null,
|
|
27
|
+
input: normalizeUnknown(event.input),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
if (event.type !== 'tool_result') return null;
|
|
31
|
+
return {
|
|
32
|
+
type: 'tool_result',
|
|
33
|
+
toolId: event.toolId ?? null,
|
|
34
|
+
content: normalizeUnknown(event.content),
|
|
35
|
+
isError: normalizeUnknown(event.isError),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function canonicalResult(event: OutputEvent): Record<string, unknown> {
|
|
40
|
+
if (event.type !== 'result' || typeof event.success !== 'boolean') {
|
|
41
|
+
throw new SemanticProjectionError('event_invalid');
|
|
42
|
+
}
|
|
43
|
+
const normalized: Record<string, unknown> = { type: 'result', success: event.success };
|
|
44
|
+
for (const key of RESULT_OPTIONAL_KEYS) {
|
|
45
|
+
if (event[key] !== undefined) normalized[key] = normalizeUnknown(event[key]);
|
|
46
|
+
}
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function canonicalEvent(event: OutputEvent): Record<string, unknown> {
|
|
51
|
+
if (!event || typeof event !== 'object' || typeof event.type !== 'string') {
|
|
52
|
+
throw new SemanticProjectionError('event_invalid');
|
|
53
|
+
}
|
|
54
|
+
if (event.type === 'text' || event.type === 'thinking') {
|
|
55
|
+
if (typeof event.text !== 'string') throw new SemanticProjectionError('event_invalid');
|
|
56
|
+
return { type: event.type, text: normalizeUnknown(event.text) };
|
|
57
|
+
}
|
|
58
|
+
return canonicalToolEvent(event) ?? canonicalResult(event);
|
|
59
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SEMANTIC_BOUNDS = exports.MAX_DIAGNOSTICS_PER_TASK = exports.MAX_EVENTS_PER_TASK = exports.MAX_VALUE_NODES = exports.MAX_CONTAINER_ITEMS = exports.MAX_VALUE_DEPTH = exports.MAX_STRING_BYTES = exports.MAX_EVENT_BYTES = exports.MAX_LINE_BYTES = exports.SEMANTIC_MEDIA_TYPE = exports.SEMANTIC_SCHEMA_VERSION = void 0;
|
|
4
|
+
exports.classifyOwnedLogLine = classifyOwnedLogLine;
|
|
5
|
+
exports.reportDiagnostic = reportDiagnostic;
|
|
6
|
+
const task_log_line_1 = require("../src/task-log-line");
|
|
7
|
+
exports.SEMANTIC_SCHEMA_VERSION = 'zeroshot.semantic.v1';
|
|
8
|
+
exports.SEMANTIC_MEDIA_TYPE = 'application/x-zeroshot-semantic+jsonl';
|
|
9
|
+
exports.MAX_LINE_BYTES = 1024 * 1024;
|
|
10
|
+
exports.MAX_EVENT_BYTES = 512 * 1024;
|
|
11
|
+
exports.MAX_STRING_BYTES = 256 * 1024;
|
|
12
|
+
exports.MAX_VALUE_DEPTH = 32;
|
|
13
|
+
exports.MAX_CONTAINER_ITEMS = 4096;
|
|
14
|
+
exports.MAX_VALUE_NODES = 16384;
|
|
15
|
+
exports.MAX_EVENTS_PER_TASK = 100000;
|
|
16
|
+
exports.MAX_DIAGNOSTICS_PER_TASK = 1000;
|
|
17
|
+
exports.SEMANTIC_BOUNDS = {
|
|
18
|
+
maxLineBytes: exports.MAX_LINE_BYTES,
|
|
19
|
+
maxEventBytes: exports.MAX_EVENT_BYTES,
|
|
20
|
+
maxStringBytes: exports.MAX_STRING_BYTES,
|
|
21
|
+
maxValueDepth: exports.MAX_VALUE_DEPTH,
|
|
22
|
+
maxContainerItems: exports.MAX_CONTAINER_ITEMS,
|
|
23
|
+
maxValueNodes: exports.MAX_VALUE_NODES,
|
|
24
|
+
maxEventsPerTask: exports.MAX_EVENTS_PER_TASK,
|
|
25
|
+
maxDiagnosticsPerTask: exports.MAX_DIAGNOSTICS_PER_TASK,
|
|
26
|
+
};
|
|
27
|
+
const WRAPPER_LINE_PATTERNS = [
|
|
28
|
+
/^={50}$/,
|
|
29
|
+
/^Finished: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,
|
|
30
|
+
/^Exit code: (?:-?\d+|null), Signal: (?:[A-Za-z0-9]+|null)$/,
|
|
31
|
+
/^Stop reason: [^,]+, Exit code: (?:-?\d+|null), Signal: (?:[A-Za-z0-9]+|null)$/,
|
|
32
|
+
];
|
|
33
|
+
function classifyOwnedLogLine(rawLine) {
|
|
34
|
+
const line = rawLine.trim();
|
|
35
|
+
if (!line)
|
|
36
|
+
return 'owned';
|
|
37
|
+
if (WRAPPER_LINE_PATTERNS.some((pattern) => pattern.test(line)))
|
|
38
|
+
return 'owned';
|
|
39
|
+
const decoded = (0, task_log_line_1.decodeTaskLogLine)(rawLine);
|
|
40
|
+
if (decoded.format === 'stderr-tagged-v1')
|
|
41
|
+
return 'format-v1';
|
|
42
|
+
if (decoded.format === 'channel-framed-v2')
|
|
43
|
+
return 'format-v2';
|
|
44
|
+
return decoded.providerOutput ? null : 'owned';
|
|
45
|
+
}
|
|
46
|
+
function diagnosticMessage(code) {
|
|
47
|
+
const messages = {
|
|
48
|
+
ambiguous_agent: 'Task evidence has more than one causal agent.',
|
|
49
|
+
event_invalid: 'Provider adapter emitted an invalid OutputEvent.',
|
|
50
|
+
event_limit_exceeded: 'Semantic event count exceeded the per-task bound.',
|
|
51
|
+
event_shape_too_deep: 'OutputEvent JSON exceeded the nesting bound.',
|
|
52
|
+
event_shape_too_large: 'OutputEvent JSON exceeded the node bound.',
|
|
53
|
+
event_shape_too_wide: 'OutputEvent JSON exceeded the container bound.',
|
|
54
|
+
event_string_too_large: 'OutputEvent string exceeded the byte bound.',
|
|
55
|
+
event_too_large: 'Canonical OutputEvent exceeded the byte bound.',
|
|
56
|
+
event_value_not_json: 'OutputEvent contained a non-JSON value.',
|
|
57
|
+
invalid_json_shape: 'Provider source line was not a JSON object.',
|
|
58
|
+
invalid_utf8: 'Provider source line was not valid UTF-8.',
|
|
59
|
+
legacy_ambiguous_channels: 'Legacy task log does not prove stdout and stderr provenance.',
|
|
60
|
+
line_too_large: 'Provider source line exceeded the byte bound.',
|
|
61
|
+
log_changed_during_parse: 'Raw provider output changed during semantic projection.',
|
|
62
|
+
log_changed_during_hash: 'Raw provider output changed while its digest was computed.',
|
|
63
|
+
log_missing: 'Raw provider output is missing.',
|
|
64
|
+
log_not_regular: 'Raw provider output is not a regular file.',
|
|
65
|
+
log_read_failed: 'Raw provider output could not be read completely.',
|
|
66
|
+
log_reference_invalid: 'Task raw-output reference is outside the owned log boundary.',
|
|
67
|
+
log_unreadable: 'Raw provider output could not be opened safely.',
|
|
68
|
+
malformed_json: 'Provider source line contained malformed JSON.',
|
|
69
|
+
output_after_terminal: 'Provider output followed the terminal result.',
|
|
70
|
+
parser_error: 'Provider adapter rejected a source line.',
|
|
71
|
+
parser_finish_error: 'Provider adapter failed while finishing its stateful parse.',
|
|
72
|
+
task_not_terminal: 'Task-store row was not terminal when evidence was captured.',
|
|
73
|
+
task_row_identity_mismatch: 'Task-store identity did not match causal ledger identity.',
|
|
74
|
+
task_row_missing: 'Causally referenced task-store row is missing.',
|
|
75
|
+
task_row_unreadable: 'Causally referenced task-store row could not be read.',
|
|
76
|
+
terminal_result_missing: 'Provider evidence did not produce one terminal result.',
|
|
77
|
+
unframed_log_line: 'A channel-framed task log contained an unframed source line.',
|
|
78
|
+
unknown_provider: 'Task provider has no registered Zeroshot adapter.',
|
|
79
|
+
};
|
|
80
|
+
return messages[code] || 'Semantic projection was incomplete.';
|
|
81
|
+
}
|
|
82
|
+
function reportDiagnostic(context, code, source) {
|
|
83
|
+
context.issueCodes.add(code);
|
|
84
|
+
if (context.diagnosticIndex >= exports.MAX_DIAGNOSTICS_PER_TASK) {
|
|
85
|
+
context.diagnosticsOmitted += 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
context.writer.write({
|
|
89
|
+
record_type: 'diagnostic',
|
|
90
|
+
task_id: context.taskId,
|
|
91
|
+
diagnostic_index: context.diagnosticIndex,
|
|
92
|
+
code,
|
|
93
|
+
message: diagnosticMessage(code),
|
|
94
|
+
source,
|
|
95
|
+
});
|
|
96
|
+
context.diagnosticIndex += 1;
|
|
97
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { ProviderAdapter } from './agent-provider-boundary';
|
|
2
|
+
import type { ExportStream, RecordWriter } from './export-stream';
|
|
3
|
+
import type { ClusterLedger } from './trace-evidence';
|
|
4
|
+
import type fs = require('fs');
|
|
5
|
+
import { decodeTaskLogLine } from '../src/task-log-line';
|
|
6
|
+
|
|
7
|
+
export const SEMANTIC_SCHEMA_VERSION = 'zeroshot.semantic.v1';
|
|
8
|
+
export const SEMANTIC_MEDIA_TYPE = 'application/x-zeroshot-semantic+jsonl';
|
|
9
|
+
export const MAX_LINE_BYTES = 1024 * 1024;
|
|
10
|
+
export const MAX_EVENT_BYTES = 512 * 1024;
|
|
11
|
+
export const MAX_STRING_BYTES = 256 * 1024;
|
|
12
|
+
export const MAX_VALUE_DEPTH = 32;
|
|
13
|
+
export const MAX_CONTAINER_ITEMS = 4096;
|
|
14
|
+
export const MAX_VALUE_NODES = 16384;
|
|
15
|
+
export const MAX_EVENTS_PER_TASK = 100000;
|
|
16
|
+
export const MAX_DIAGNOSTICS_PER_TASK = 1000;
|
|
17
|
+
export const SEMANTIC_BOUNDS = {
|
|
18
|
+
maxLineBytes: MAX_LINE_BYTES,
|
|
19
|
+
maxEventBytes: MAX_EVENT_BYTES,
|
|
20
|
+
maxStringBytes: MAX_STRING_BYTES,
|
|
21
|
+
maxValueDepth: MAX_VALUE_DEPTH,
|
|
22
|
+
maxContainerItems: MAX_CONTAINER_ITEMS,
|
|
23
|
+
maxValueNodes: MAX_VALUE_NODES,
|
|
24
|
+
maxEventsPerTask: MAX_EVENTS_PER_TASK,
|
|
25
|
+
maxDiagnosticsPerTask: MAX_DIAGNOSTICS_PER_TASK,
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
const WRAPPER_LINE_PATTERNS = [
|
|
29
|
+
/^={50}$/,
|
|
30
|
+
/^Finished: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/,
|
|
31
|
+
/^Exit code: (?:-?\d+|null), Signal: (?:[A-Za-z0-9]+|null)$/,
|
|
32
|
+
/^Stop reason: [^,]+, Exit code: (?:-?\d+|null), Signal: (?:[A-Za-z0-9]+|null)$/,
|
|
33
|
+
] as const;
|
|
34
|
+
export interface SemanticTask {
|
|
35
|
+
id?: unknown;
|
|
36
|
+
fullPrompt?: unknown;
|
|
37
|
+
prompt?: unknown;
|
|
38
|
+
status?: unknown;
|
|
39
|
+
provider?: unknown;
|
|
40
|
+
model?: unknown;
|
|
41
|
+
logFile?: unknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SemanticExportOptions {
|
|
45
|
+
ledger: ClusterLedger;
|
|
46
|
+
clusterId: string;
|
|
47
|
+
readTask(taskId: string): SemanticTask | null;
|
|
48
|
+
allowedLogRoot: string;
|
|
49
|
+
outputPath?: string | null;
|
|
50
|
+
stdout?: ExportStream;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SourceSpan {
|
|
54
|
+
line_number: number;
|
|
55
|
+
byte_start: number;
|
|
56
|
+
byte_end: number;
|
|
57
|
+
timestamp_ms: number | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface TaskProjection {
|
|
61
|
+
writer: RecordWriter;
|
|
62
|
+
taskId: string;
|
|
63
|
+
provider: string | null;
|
|
64
|
+
adapter: ProviderAdapter | null;
|
|
65
|
+
promptRef: string;
|
|
66
|
+
rawOutputRef: string;
|
|
67
|
+
rawOutputSha256: string | null;
|
|
68
|
+
eventIndex: number;
|
|
69
|
+
diagnosticIndex: number;
|
|
70
|
+
diagnosticsOmitted: number;
|
|
71
|
+
issueCodes: Set<string>;
|
|
72
|
+
logFormat: TaskLogFormat | null;
|
|
73
|
+
terminalResultEmitted: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface OpenedEvidence {
|
|
77
|
+
fd: number | null;
|
|
78
|
+
before: fs.BigIntStats | null;
|
|
79
|
+
byteLength: number | null;
|
|
80
|
+
sha256: string | null;
|
|
81
|
+
issue: string | null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
type TaskLogFormat = 'legacy-pi' | 'stderr-tagged-v1' | 'channel-framed-v2';
|
|
85
|
+
export type OwnedLogLine = 'format-v1' | 'format-v2' | 'owned' | null;
|
|
86
|
+
|
|
87
|
+
export function classifyOwnedLogLine(rawLine: string): OwnedLogLine {
|
|
88
|
+
const line = rawLine.trim();
|
|
89
|
+
if (!line) return 'owned';
|
|
90
|
+
if (WRAPPER_LINE_PATTERNS.some((pattern) => pattern.test(line))) return 'owned';
|
|
91
|
+
const decoded = decodeTaskLogLine(rawLine);
|
|
92
|
+
if (decoded.format === 'stderr-tagged-v1') return 'format-v1';
|
|
93
|
+
if (decoded.format === 'channel-framed-v2') return 'format-v2';
|
|
94
|
+
return decoded.providerOutput ? null : 'owned';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function diagnosticMessage(code: string): string {
|
|
98
|
+
const messages: Record<string, string> = {
|
|
99
|
+
ambiguous_agent: 'Task evidence has more than one causal agent.',
|
|
100
|
+
event_invalid: 'Provider adapter emitted an invalid OutputEvent.',
|
|
101
|
+
event_limit_exceeded: 'Semantic event count exceeded the per-task bound.',
|
|
102
|
+
event_shape_too_deep: 'OutputEvent JSON exceeded the nesting bound.',
|
|
103
|
+
event_shape_too_large: 'OutputEvent JSON exceeded the node bound.',
|
|
104
|
+
event_shape_too_wide: 'OutputEvent JSON exceeded the container bound.',
|
|
105
|
+
event_string_too_large: 'OutputEvent string exceeded the byte bound.',
|
|
106
|
+
event_too_large: 'Canonical OutputEvent exceeded the byte bound.',
|
|
107
|
+
event_value_not_json: 'OutputEvent contained a non-JSON value.',
|
|
108
|
+
invalid_json_shape: 'Provider source line was not a JSON object.',
|
|
109
|
+
invalid_utf8: 'Provider source line was not valid UTF-8.',
|
|
110
|
+
legacy_ambiguous_channels: 'Legacy task log does not prove stdout and stderr provenance.',
|
|
111
|
+
line_too_large: 'Provider source line exceeded the byte bound.',
|
|
112
|
+
log_changed_during_parse: 'Raw provider output changed during semantic projection.',
|
|
113
|
+
log_changed_during_hash: 'Raw provider output changed while its digest was computed.',
|
|
114
|
+
log_missing: 'Raw provider output is missing.',
|
|
115
|
+
log_not_regular: 'Raw provider output is not a regular file.',
|
|
116
|
+
log_read_failed: 'Raw provider output could not be read completely.',
|
|
117
|
+
log_reference_invalid: 'Task raw-output reference is outside the owned log boundary.',
|
|
118
|
+
log_unreadable: 'Raw provider output could not be opened safely.',
|
|
119
|
+
malformed_json: 'Provider source line contained malformed JSON.',
|
|
120
|
+
output_after_terminal: 'Provider output followed the terminal result.',
|
|
121
|
+
parser_error: 'Provider adapter rejected a source line.',
|
|
122
|
+
parser_finish_error: 'Provider adapter failed while finishing its stateful parse.',
|
|
123
|
+
task_not_terminal: 'Task-store row was not terminal when evidence was captured.',
|
|
124
|
+
task_row_identity_mismatch: 'Task-store identity did not match causal ledger identity.',
|
|
125
|
+
task_row_missing: 'Causally referenced task-store row is missing.',
|
|
126
|
+
task_row_unreadable: 'Causally referenced task-store row could not be read.',
|
|
127
|
+
terminal_result_missing: 'Provider evidence did not produce one terminal result.',
|
|
128
|
+
unframed_log_line: 'A channel-framed task log contained an unframed source line.',
|
|
129
|
+
unknown_provider: 'Task provider has no registered Zeroshot adapter.',
|
|
130
|
+
};
|
|
131
|
+
return messages[code] || 'Semantic projection was incomplete.';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function reportDiagnostic(
|
|
135
|
+
context: TaskProjection,
|
|
136
|
+
code: string,
|
|
137
|
+
source: SourceSpan | null
|
|
138
|
+
): void {
|
|
139
|
+
context.issueCodes.add(code);
|
|
140
|
+
if (context.diagnosticIndex >= MAX_DIAGNOSTICS_PER_TASK) {
|
|
141
|
+
context.diagnosticsOmitted += 1;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
context.writer.write({
|
|
145
|
+
record_type: 'diagnostic',
|
|
146
|
+
task_id: context.taskId,
|
|
147
|
+
diagnostic_index: context.diagnosticIndex,
|
|
148
|
+
code,
|
|
149
|
+
message: diagnosticMessage(code),
|
|
150
|
+
source,
|
|
151
|
+
});
|
|
152
|
+
context.diagnosticIndex += 1;
|
|
153
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emitEvent = emitEvent;
|
|
4
|
+
exports.emittedEvents = emittedEvents;
|
|
5
|
+
const semantic_canonical_1 = require("./semantic-canonical");
|
|
6
|
+
const semantic_contract_1 = require("./semantic-contract");
|
|
7
|
+
const semantic_json_1 = require("./semantic-json");
|
|
8
|
+
function emitEvent(context, event, source, derivation) {
|
|
9
|
+
if (context.terminalResultEmitted) {
|
|
10
|
+
(0, semantic_contract_1.reportDiagnostic)(context, 'output_after_terminal', source);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (context.eventIndex >= semantic_contract_1.MAX_EVENTS_PER_TASK) {
|
|
14
|
+
if (!context.issueCodes.has('event_limit_exceeded')) {
|
|
15
|
+
(0, semantic_contract_1.reportDiagnostic)(context, 'event_limit_exceeded', source);
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const normalized = (0, semantic_canonical_1.canonicalEvent)(event);
|
|
21
|
+
if (Buffer.byteLength(JSON.stringify(normalized)) > semantic_contract_1.MAX_EVENT_BYTES) {
|
|
22
|
+
throw new semantic_json_1.SemanticProjectionError('event_too_large');
|
|
23
|
+
}
|
|
24
|
+
context.writer.write({
|
|
25
|
+
record_type: 'event',
|
|
26
|
+
task_id: context.taskId,
|
|
27
|
+
event_index: context.eventIndex,
|
|
28
|
+
provider: context.provider,
|
|
29
|
+
adapter_id: context.adapter?.id ?? null,
|
|
30
|
+
adapter_version: context.adapter?.adapterVersion ?? null,
|
|
31
|
+
prompt_ref: context.promptRef,
|
|
32
|
+
raw_output_ref: context.rawOutputRef,
|
|
33
|
+
raw_output_sha256: context.rawOutputSha256,
|
|
34
|
+
derivation,
|
|
35
|
+
source,
|
|
36
|
+
event: normalized,
|
|
37
|
+
});
|
|
38
|
+
context.eventIndex += 1;
|
|
39
|
+
if (normalized.type === 'result')
|
|
40
|
+
context.terminalResultEmitted = true;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
(0, semantic_contract_1.reportDiagnostic)(context, error instanceof semantic_json_1.SemanticProjectionError ? error.code : 'event_invalid', source);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function emittedEvents(result) {
|
|
47
|
+
if (result === null)
|
|
48
|
+
return [];
|
|
49
|
+
return isEventArray(result) ? result : [result];
|
|
50
|
+
}
|
|
51
|
+
function isEventArray(result) {
|
|
52
|
+
return Array.isArray(result);
|
|
53
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { OutputEvent, ProviderParseResult } from './agent-provider-boundary';
|
|
2
|
+
import { canonicalEvent } from './semantic-canonical';
|
|
3
|
+
import {
|
|
4
|
+
MAX_EVENTS_PER_TASK,
|
|
5
|
+
MAX_EVENT_BYTES,
|
|
6
|
+
reportDiagnostic,
|
|
7
|
+
type SourceSpan,
|
|
8
|
+
type TaskProjection,
|
|
9
|
+
} from './semantic-contract';
|
|
10
|
+
import { SemanticProjectionError } from './semantic-json';
|
|
11
|
+
|
|
12
|
+
export function emitEvent(
|
|
13
|
+
context: TaskProjection,
|
|
14
|
+
event: OutputEvent,
|
|
15
|
+
source: SourceSpan | null,
|
|
16
|
+
derivation: 'line' | 'finish'
|
|
17
|
+
): void {
|
|
18
|
+
if (context.terminalResultEmitted) {
|
|
19
|
+
reportDiagnostic(context, 'output_after_terminal', source);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (context.eventIndex >= MAX_EVENTS_PER_TASK) {
|
|
23
|
+
if (!context.issueCodes.has('event_limit_exceeded')) {
|
|
24
|
+
reportDiagnostic(context, 'event_limit_exceeded', source);
|
|
25
|
+
}
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const normalized = canonicalEvent(event);
|
|
30
|
+
if (Buffer.byteLength(JSON.stringify(normalized)) > MAX_EVENT_BYTES) {
|
|
31
|
+
throw new SemanticProjectionError('event_too_large');
|
|
32
|
+
}
|
|
33
|
+
context.writer.write({
|
|
34
|
+
record_type: 'event',
|
|
35
|
+
task_id: context.taskId,
|
|
36
|
+
event_index: context.eventIndex,
|
|
37
|
+
provider: context.provider,
|
|
38
|
+
adapter_id: context.adapter?.id ?? null,
|
|
39
|
+
adapter_version: context.adapter?.adapterVersion ?? null,
|
|
40
|
+
prompt_ref: context.promptRef,
|
|
41
|
+
raw_output_ref: context.rawOutputRef,
|
|
42
|
+
raw_output_sha256: context.rawOutputSha256,
|
|
43
|
+
derivation,
|
|
44
|
+
source,
|
|
45
|
+
event: normalized,
|
|
46
|
+
});
|
|
47
|
+
context.eventIndex += 1;
|
|
48
|
+
if (normalized.type === 'result') context.terminalResultEmitted = true;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
reportDiagnostic(
|
|
51
|
+
context,
|
|
52
|
+
error instanceof SemanticProjectionError ? error.code : 'event_invalid',
|
|
53
|
+
source
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function emittedEvents(result: ProviderParseResult): readonly OutputEvent[] {
|
|
59
|
+
if (result === null) return [];
|
|
60
|
+
return isEventArray(result) ? result : [result];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function isEventArray(result: ProviderParseResult): result is readonly OutputEvent[] {
|
|
64
|
+
return Array.isArray(result);
|
|
65
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.openEvidence = openEvidence;
|
|
4
|
+
exports.emptyEvidence = emptyEvidence;
|
|
5
|
+
const crypto = require("crypto");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const export_stream_1 = require("./export-stream");
|
|
9
|
+
const trace_evidence_1 = require("./trace-evidence");
|
|
10
|
+
const trace_output_1 = require("./trace-output");
|
|
11
|
+
function hashOpenedEvidence(fd, byteLength) {
|
|
12
|
+
const digest = crypto.createHash('sha256');
|
|
13
|
+
let bytes = 0;
|
|
14
|
+
while (bytes < byteLength) {
|
|
15
|
+
const buffer = Buffer.allocUnsafe(Math.min(trace_output_1.TRACE_OUTPUT_CHUNK_BYTES, byteLength - bytes));
|
|
16
|
+
const read = fs.readSync(fd, buffer, 0, buffer.length, bytes);
|
|
17
|
+
if (read === 0)
|
|
18
|
+
break;
|
|
19
|
+
digest.update(read === buffer.length ? buffer : buffer.subarray(0, read));
|
|
20
|
+
bytes += read;
|
|
21
|
+
}
|
|
22
|
+
return { bytes, sha256: digest.digest('hex') };
|
|
23
|
+
}
|
|
24
|
+
function unavailable(issue) {
|
|
25
|
+
return { fd: null, before: null, byteLength: null, sha256: null, issue };
|
|
26
|
+
}
|
|
27
|
+
function openEvidence(taskId, task, allowedLogRoot) {
|
|
28
|
+
const expected = (0, trace_evidence_1.expectedLogPath)(allowedLogRoot, taskId);
|
|
29
|
+
const recorded = (0, export_stream_1.nullableString)(task.logFile);
|
|
30
|
+
if (!expected || !recorded || path.resolve(recorded) !== expected) {
|
|
31
|
+
return unavailable('log_reference_invalid');
|
|
32
|
+
}
|
|
33
|
+
let fd;
|
|
34
|
+
try {
|
|
35
|
+
fd = fs.openSync(expected, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : null;
|
|
39
|
+
return unavailable(code === 'ENOENT' ? 'log_missing' : 'log_unreadable');
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const before = fs.fstatSync(fd, { bigint: true });
|
|
43
|
+
if (!before.isFile() || before.size > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
44
|
+
fs.closeSync(fd);
|
|
45
|
+
return unavailable('log_not_regular');
|
|
46
|
+
}
|
|
47
|
+
const byteLength = Number(before.size);
|
|
48
|
+
const hashed = hashOpenedEvidence(fd, byteLength);
|
|
49
|
+
const afterHash = fs.fstatSync(fd, { bigint: true });
|
|
50
|
+
if (hashed.bytes !== byteLength || !(0, export_stream_1.sameFileSnapshot)(before, afterHash)) {
|
|
51
|
+
fs.closeSync(fd);
|
|
52
|
+
return { ...unavailable('log_changed_during_hash'), byteLength };
|
|
53
|
+
}
|
|
54
|
+
return { fd, before, byteLength, sha256: hashed.sha256, issue: null };
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
fs.closeSync(fd);
|
|
58
|
+
return unavailable('log_read_failed');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function emptyEvidence() {
|
|
62
|
+
return { fd: null, before: null, byteLength: null, sha256: null, issue: null };
|
|
63
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import crypto = require('crypto');
|
|
2
|
+
import fs = require('fs');
|
|
3
|
+
import path = require('path');
|
|
4
|
+
import { nullableString, sameFileSnapshot } from './export-stream';
|
|
5
|
+
import type { OpenedEvidence, SemanticTask } from './semantic-contract';
|
|
6
|
+
import { expectedLogPath } from './trace-evidence';
|
|
7
|
+
import { TRACE_OUTPUT_CHUNK_BYTES } from './trace-output';
|
|
8
|
+
|
|
9
|
+
function hashOpenedEvidence(fd: number, byteLength: number): { bytes: number; sha256: string } {
|
|
10
|
+
const digest = crypto.createHash('sha256');
|
|
11
|
+
let bytes = 0;
|
|
12
|
+
while (bytes < byteLength) {
|
|
13
|
+
const buffer = Buffer.allocUnsafe(Math.min(TRACE_OUTPUT_CHUNK_BYTES, byteLength - bytes));
|
|
14
|
+
const read = fs.readSync(fd, buffer, 0, buffer.length, bytes);
|
|
15
|
+
if (read === 0) break;
|
|
16
|
+
digest.update(read === buffer.length ? buffer : buffer.subarray(0, read));
|
|
17
|
+
bytes += read;
|
|
18
|
+
}
|
|
19
|
+
return { bytes, sha256: digest.digest('hex') };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function unavailable(issue: string): OpenedEvidence {
|
|
23
|
+
return { fd: null, before: null, byteLength: null, sha256: null, issue };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function openEvidence(
|
|
27
|
+
taskId: string,
|
|
28
|
+
task: SemanticTask,
|
|
29
|
+
allowedLogRoot: string
|
|
30
|
+
): OpenedEvidence {
|
|
31
|
+
const expected = expectedLogPath(allowedLogRoot, taskId);
|
|
32
|
+
const recorded = nullableString(task.logFile);
|
|
33
|
+
if (!expected || !recorded || path.resolve(recorded) !== expected) {
|
|
34
|
+
return unavailable('log_reference_invalid');
|
|
35
|
+
}
|
|
36
|
+
let fd: number;
|
|
37
|
+
try {
|
|
38
|
+
fd = fs.openSync(expected, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0));
|
|
39
|
+
} catch (error) {
|
|
40
|
+
const code = typeof error === 'object' && error !== null && 'code' in error ? error.code : null;
|
|
41
|
+
return unavailable(code === 'ENOENT' ? 'log_missing' : 'log_unreadable');
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const before = fs.fstatSync(fd, { bigint: true });
|
|
45
|
+
if (!before.isFile() || before.size > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
46
|
+
fs.closeSync(fd);
|
|
47
|
+
return unavailable('log_not_regular');
|
|
48
|
+
}
|
|
49
|
+
const byteLength = Number(before.size);
|
|
50
|
+
const hashed = hashOpenedEvidence(fd, byteLength);
|
|
51
|
+
const afterHash = fs.fstatSync(fd, { bigint: true });
|
|
52
|
+
if (hashed.bytes !== byteLength || !sameFileSnapshot(before, afterHash)) {
|
|
53
|
+
fs.closeSync(fd);
|
|
54
|
+
return { ...unavailable('log_changed_during_hash'), byteLength };
|
|
55
|
+
}
|
|
56
|
+
return { fd, before, byteLength, sha256: hashed.sha256, issue: null };
|
|
57
|
+
} catch {
|
|
58
|
+
fs.closeSync(fd);
|
|
59
|
+
return unavailable('log_read_failed');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function emptyEvidence(): OpenedEvidence {
|
|
64
|
+
return { fd: null, before: null, byteLength: null, sha256: null, issue: null };
|
|
65
|
+
}
|