solsticestudioai commited on
Commit
eeb63f8
·
verified ·
1 Parent(s): f287f86

Add DeltaStore exchange v1 support

Browse files
Files changed (1) hide show
  1. atlas-core.js +72 -0
atlas-core.js CHANGED
@@ -35,6 +35,51 @@ const SEVERITY_RANK = { info: 0, low: 1, medium: 2, high: 3, critical: 4 };
35
 
36
  export class TraceFormatError extends Error {}
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  function clonePolicy(policy = {}) {
39
  return {
40
  ...structuredClone(DEFAULT_POLICY),
@@ -112,6 +157,16 @@ export function parseTraceText(text, filename = "trace.jsonl", limits = {}) {
112
  const maxRows = limits.maxRows ?? 50_000;
113
  if (encoder.encode(text).byteLength > maxFileBytes) throw new TraceFormatError(`Trace exceeds ${maxFileBytes} bytes`);
114
 
 
 
 
 
 
 
 
 
 
 
115
  const lines = text.split(/\r?\n/);
116
  const rows = [];
117
  for (let i = 0; i < lines.length; i += 1) {
@@ -127,6 +182,15 @@ export function parseTraceText(text, filename = "trace.jsonl", limits = {}) {
127
  }
128
  if (!rows.length) throw new TraceFormatError("Trace file is empty");
129
 
 
 
 
 
 
 
 
 
 
130
  let session;
131
  let format;
132
  if (rows[0].type === "session") {
@@ -418,6 +482,14 @@ export async function scanSession(session, rawPolicy = {}) {
418
  ]);
419
  const byId = new Map(groups.flat().map((finding) => [finding.id, finding]));
420
  const findings = [...byId.values()].sort((a, b) => (SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]) || ((a.evidence[0]?.message_index ?? 1e9) - (b.evidence[0]?.message_index ?? 1e9)) || a.id.localeCompare(b.id));
 
 
 
 
 
 
 
 
421
  const findingsBySeverity = {};
422
  const findingsByCategory = {};
423
  for (const finding of findings) {
 
35
 
36
  export class TraceFormatError extends Error {}
37
 
38
+ const DELTASTORE_EXCHANGE_VERSION = "solstice-agent-trace-exchange/v1";
39
+
40
+ function validateDeltaStoreExchange(value) {
41
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new TraceFormatError("DeltaStore exchange must be a JSON object");
42
+ if (value.schema_version !== DELTASTORE_EXCHANGE_VERSION) throw new TraceFormatError(`Unsupported DeltaStore exchange version: ${value.schema_version ?? "missing"}`);
43
+ const required = ["trace_id", "source", "task", "events", "checkpoints", "branches", "outcomes", "findings", "redaction", "provenance"];
44
+ const missing = required.filter((key) => !(key in value));
45
+ if (missing.length) throw new TraceFormatError(`DeltaStore exchange missing fields: ${missing.join(", ")}`);
46
+ if (!Array.isArray(value.events) || !Array.isArray(value.checkpoints) || !Array.isArray(value.branches)) throw new TraceFormatError("DeltaStore events, checkpoints, and branches must be arrays");
47
+ const ids = value.events.map((event) => event?.event_id);
48
+ if (ids.some((id) => !id) || new Set(ids).size !== ids.length) throw new TraceFormatError("Duplicate or missing DeltaStore event ID");
49
+ const sequences = value.events.map((event) => event?.sequence);
50
+ if (sequences.some((sequence) => !Number.isInteger(sequence)) || sequences.some((sequence, index) => index && sequence <= sequences[index - 1])) throw new TraceFormatError("DeltaStore event sequence must be strictly increasing");
51
+ const toolIds = value.events.map((event) => event?.tool_call_id).filter(Boolean);
52
+ if (new Set(toolIds).size !== toolIds.length) throw new TraceFormatError("Duplicate DeltaStore tool-call ID");
53
+ const checkpointIds = new Set(value.checkpoints.map((item) => item?.checkpoint_id));
54
+ if (checkpointIds.size !== value.checkpoints.length || [...checkpointIds].some((id) => !id)) throw new TraceFormatError("Duplicate or invalid DeltaStore checkpoint ID");
55
+ for (const checkpoint of value.checkpoints) if (!ids.includes(checkpoint.event_id)) throw new TraceFormatError("Checkpoint references unknown event");
56
+ for (const event of value.events) {
57
+ if (event.parent_event_id && !ids.includes(event.parent_event_id)) throw new TraceFormatError("DeltaStore event references unknown parent_event_id");
58
+ for (const ref of event.evidence_refs ?? []) if (!ids.includes(ref)) throw new TraceFormatError("DeltaStore event references unknown evidence event");
59
+ }
60
+ return value;
61
+ }
62
+
63
+ function parseDeltaStoreExchange(value, filename) {
64
+ const envelope = validateDeltaStoreExchange(value);
65
+ const eventIds = envelope.events.map((event) => event.event_id);
66
+ const messages = envelope.events.map((event) => {
67
+ const eventType = String(event.event_type);
68
+ const role = String(event.actor?.role ?? event.actor?.type ?? (eventType === "tool_result" || eventType === "tool_output" ? "tool" : "assistant"));
69
+ const message = { role, content: normalizeContent(event.output_summary ?? event.input_summary ?? event.message ?? ""), source_event_type: eventType, event_id: event.event_id, branch_id: event.branch_id ?? null, checkpoint_id: event.checkpoint_id ?? null };
70
+ if (eventType === "tool_call" || eventType === "tool_use") message.toolCalls = [{ id: String(event.tool_call_id ?? event.event_id), function: { name: String(event.tool_name ?? "unknown_tool"), arguments: event.input_summary ?? {} } }];
71
+ if (eventType === "tool_result" || eventType === "tool_output") message.toolCallId = String(event.tool_call_id ?? "");
72
+ return normalizeMessage(message);
73
+ });
74
+ return {
75
+ session: {
76
+ harness: "deltastore-exchange", id: String(envelope.trace_id), name: envelope.title ?? null, messages,
77
+ metadata: { source_format: DELTASTORE_EXCHANGE_VERSION, source_schema_version: DELTASTORE_EXCHANGE_VERSION, trace_id: envelope.trace_id, exchange_event_ids: eventIds, exchange_checkpoints: envelope.checkpoints, exchange_branches: envelope.branches, exchange_findings: envelope.findings ?? [], exchange_policy: envelope.policy ?? {}, exchange_provenance: envelope.provenance, exchange_redaction: envelope.redaction, normalization_report: { source_events: messages.length, normalized_events: messages.length, dropped_events: [], unsupported_event_types: [], inferred_fields: [], preserved_tool_calls: messages.filter((message) => message.toolCalls.length).length, preserved_tool_results: messages.filter((message) => message.role === "tool").length } }, source: filename,
78
+ },
79
+ format: "DeltaStore Trace Exchange v1",
80
+ };
81
+ }
82
+
83
  function clonePolicy(policy = {}) {
84
  return {
85
  ...structuredClone(DEFAULT_POLICY),
 
157
  const maxRows = limits.maxRows ?? 50_000;
158
  if (encoder.encode(text).byteLength > maxFileBytes) throw new TraceFormatError(`Trace exceeds ${maxFileBytes} bytes`);
159
 
160
+ if (text.trimStart().startsWith("{")) {
161
+ try {
162
+ const value = JSON.parse(text);
163
+ if (value && typeof value === "object" && value.schema_version === DELTASTORE_EXCHANGE_VERSION) return parseDeltaStoreExchange(value, filename);
164
+ } catch (error) {
165
+ if (error instanceof TraceFormatError) throw error;
166
+ // JSONL formats begin with an object too; let the line parser handle them.
167
+ }
168
+ }
169
+
170
  const lines = text.split(/\r?\n/);
171
  const rows = [];
172
  for (let i = 0; i < lines.length; i += 1) {
 
182
  }
183
  if (!rows.length) throw new TraceFormatError("Trace file is empty");
184
 
185
+ if (text.trimStart().startsWith("{")) {
186
+ try {
187
+ const value = JSON.parse(text);
188
+ if (value && typeof value === "object" && value.schema_version === DELTASTORE_EXCHANGE_VERSION) return parseDeltaStoreExchange(value, filename);
189
+ } catch (error) {
190
+ if (error instanceof TraceFormatError) throw error;
191
+ }
192
+ }
193
+
194
  let session;
195
  let format;
196
  if (rows[0].type === "session") {
 
482
  ]);
483
  const byId = new Map(groups.flat().map((finding) => [finding.id, finding]));
484
  const findings = [...byId.values()].sort((a, b) => (SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity]) || ((a.evidence[0]?.message_index ?? 1e9) - (b.evidence[0]?.message_index ?? 1e9)) || a.id.localeCompare(b.id));
485
+ if (session.metadata?.source_format === DELTASTORE_EXCHANGE_VERSION) {
486
+ const eventIds = session.metadata.exchange_event_ids ?? [];
487
+ for (const item of session.metadata.exchange_findings ?? []) {
488
+ const refs = item.evidence_event_ids ?? item.evidence_lineage ?? [];
489
+ const evidence = refs.filter((ref) => eventIds.includes(ref)).map((ref) => ({ message_index: eventIds.indexOf(ref), role: "evaluator", excerpt: `DeltaStore evidence: ${item.category ?? "finding"}` }));
490
+ findings.push({ id: String(item.finding_id ?? item.id ?? `deltastore-${item.category ?? "finding"}`), detector: "deltastore-evaluator", category: String(item.category ?? "unknown"), severity: String(item.severity ?? "medium"), title: String(item.category ?? "DeltaStore evaluator finding"), description: String(item.message ?? item.description ?? "Evidence-linked DeltaStore finding."), remediation: "Review the linked DeltaStore evidence and policy outcome.", confidence: 1, evidence, trace_id: session.id, taxonomy_version: "agent-failure-atlas/v1", rule_id: String(item.rule_id ?? "deltastore-evaluator"), evidence_event_ids: refs, branch_id: item.branch_id ?? null, checkpoint_id: item.checkpoint_id ?? null });
491
+ }
492
+ }
493
  const findingsBySeverity = {};
494
  const findingsByCategory = {};
495
  for (const finding of findings) {