@opengeni/db 6.0.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/canonical-human-identities.js +3 -3
  2. package/dist/capability-reconciliation.js +2 -2
  3. package/dist/child-read-reconciliation.d.ts +35 -0
  4. package/dist/{chunk-CJI6G254.js → chunk-4CCFFLDZ.js} +2 -2
  5. package/dist/{chunk-7JCDSMX2.js → chunk-4RDUZ5UV.js} +2 -2
  6. package/dist/{chunk-SYKHLPTK.js → chunk-6ORJPCYW.js} +2 -2
  7. package/dist/{chunk-GABXCG3N.js → chunk-CSVHCF3R.js} +11 -6
  8. package/dist/chunk-CSVHCF3R.js.map +1 -0
  9. package/dist/{chunk-CDAT6EKW.js → chunk-FULLE5CF.js} +3 -3
  10. package/dist/{chunk-MN5PZMEK.js → chunk-GSB4ISHE.js} +905 -801
  11. package/dist/chunk-GSB4ISHE.js.map +1 -0
  12. package/dist/{chunk-XIXVVBW2.js → chunk-H7BC444C.js} +3 -1
  13. package/dist/chunk-H7BC444C.js.map +1 -0
  14. package/dist/{chunk-LVY33EWL.js → chunk-I7HQL6FK.js} +3 -3
  15. package/dist/{chunk-RIYHP2WU.js → chunk-JILAWE26.js} +2 -2
  16. package/dist/{chunk-ZPJ6V2VG.js → chunk-LO6BVXXH.js} +2 -2
  17. package/dist/{chunk-PJI4MUZ7.js → chunk-LSLNMENV.js} +4 -4
  18. package/dist/{chunk-RL2MUXM3.js → chunk-PGIFHEBA.js} +2 -2
  19. package/dist/{chunk-G32NVMRJ.js → chunk-RXH5CFG3.js} +5 -5
  20. package/dist/{chunk-AJA2ZIUW.js → chunk-XK6HXON2.js} +2 -2
  21. package/dist/connect-attempts.js +3 -3
  22. package/dist/editable-artifact-durable-export.js +2 -2
  23. package/dist/editable-artifacts.js +3 -3
  24. package/dist/index.d.ts +31 -0
  25. package/dist/index.js +6651 -6244
  26. package/dist/index.js.map +1 -1
  27. package/dist/integration-oauth-pending-states.d.ts +14 -0
  28. package/dist/managed-auth-session-sets.js +3 -3
  29. package/dist/mcp-operations.js +2 -2
  30. package/dist/new-session-drafts.d.ts +2 -1
  31. package/dist/organization-integration-policy.js +3 -3
  32. package/dist/provision-roles.js +1 -1
  33. package/dist/retained-provider-commands.js +6 -6
  34. package/dist/runtime-posture.d.ts +2 -2
  35. package/dist/schema.d.ts +151 -8
  36. package/dist/schema.js +3 -1
  37. package/dist/session-background-commands.js +4 -4
  38. package/dist/session-command-output.js +5 -5
  39. package/dist/session-event-slices.js +3 -3
  40. package/dist/session-mcp-credential-rotation.js +4 -4
  41. package/dist/session-meaningful-events.d.ts +15 -0
  42. package/dist/session-tenancy.js +3 -3
  43. package/dist/skills.d.ts +2 -0
  44. package/dist/video-generation.js +3 -3
  45. package/dist/workspace-tool-defaults.js +4 -4
  46. package/drizzle/0503_session_meaningful_attention.sql +81 -0
  47. package/drizzle/0504_skill_folder_limits.sql +26 -0
  48. package/drizzle/0505_workspace_control_revision_frontier.sql +37 -0
  49. package/drizzle/0506_background_command_text.sql +3 -0
  50. package/drizzle/0507_integration_oauth_pending_states.sql +27 -0
  51. package/package.json +5 -5
  52. package/src/child-read-reconciliation.ts +170 -0
  53. package/src/index.ts +439 -60
  54. package/src/integration-oauth-pending-states.ts +52 -0
  55. package/src/new-session-drafts.ts +4 -2
  56. package/src/runtime-posture.ts +2 -0
  57. package/src/schema.ts +35 -3
  58. package/src/session-background-commands.ts +8 -2
  59. package/src/session-meaningful-events.ts +97 -0
  60. package/src/skills.ts +14 -1
  61. package/dist/chunk-GABXCG3N.js.map +0 -1
  62. package/dist/chunk-MN5PZMEK.js.map +0 -1
  63. package/dist/chunk-XIXVVBW2.js.map +0 -1
  64. /package/dist/{chunk-CJI6G254.js.map → chunk-4CCFFLDZ.js.map} +0 -0
  65. /package/dist/{chunk-7JCDSMX2.js.map → chunk-4RDUZ5UV.js.map} +0 -0
  66. /package/dist/{chunk-SYKHLPTK.js.map → chunk-6ORJPCYW.js.map} +0 -0
  67. /package/dist/{chunk-CDAT6EKW.js.map → chunk-FULLE5CF.js.map} +0 -0
  68. /package/dist/{chunk-LVY33EWL.js.map → chunk-I7HQL6FK.js.map} +0 -0
  69. /package/dist/{chunk-RIYHP2WU.js.map → chunk-JILAWE26.js.map} +0 -0
  70. /package/dist/{chunk-ZPJ6V2VG.js.map → chunk-LO6BVXXH.js.map} +0 -0
  71. /package/dist/{chunk-PJI4MUZ7.js.map → chunk-LSLNMENV.js.map} +0 -0
  72. /package/dist/{chunk-RL2MUXM3.js.map → chunk-PGIFHEBA.js.map} +0 -0
  73. /package/dist/{chunk-G32NVMRJ.js.map → chunk-RXH5CFG3.js.map} +0 -0
  74. /package/dist/{chunk-AJA2ZIUW.js.map → chunk-XK6HXON2.js.map} +0 -0
@@ -0,0 +1,81 @@
1
+ -- deployment-mode: maintenance
2
+ -- Old attention writers do not preserve explicit unread intent. Drain them;
3
+ -- do not restart pre-0503 API/control/turn workers after this migration.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '5min';
6
+ DO $drain$
7
+ DECLARE roles jsonb := nullif(current_setting('opengeni.migration_application_roles', true), '')::jsonb;
8
+ BEGIN
9
+ IF roles IS NULL OR jsonb_typeof(roles) <> 'array' OR jsonb_array_length(roles) NOT BETWEEN 1 AND 16
10
+ OR EXISTS (SELECT 1 FROM jsonb_array_elements(roles) item WHERE jsonb_typeof(item) <> 'string'
11
+ OR btrim(item #>> '{}') = '' OR item #>> '{}' <> btrim(item #>> '{}') OR octet_length(item #>> '{}') > 63)
12
+ OR EXISTS (SELECT 1 FROM pg_stat_activity a JOIN jsonb_array_elements_text(roles) r ON r = a.usename
13
+ WHERE a.datname = current_database() AND a.pid <> pg_backend_pid()) THEN
14
+ RAISE EXCEPTION 'meaningful attention migration requires stopped application roles' USING ERRCODE = '55000';
15
+ END IF;
16
+ END $drain$;
17
+
18
+ ALTER TABLE session_pins ADD COLUMN manually_unread_through integer;
19
+
20
+ -- One reverse probe per rail/tree row; raw deltas and housekeeping never enter
21
+ -- this index. Keep the predicate aligned with session-meaningful-events.ts.
22
+ CREATE INDEX session_events_meaningful_attention_idx
23
+ ON session_events (workspace_id, session_id, sequence)
24
+ WHERE type IN (
25
+ 'agent.message.completed', 'turn.completed', 'turn.failed',
26
+ 'session.requiresAction', 'session.humanInput.requested',
27
+ 'tool.auth_needed', 'credential.auth_needed', 'goal.completed', 'goal.paused',
28
+ 'goal.progress', 'goal.rewrite.proposed', 'rig.setup.failed',
29
+ 'sandbox.operation.failed', 'sandbox.box.lost', 'workspace.revision.degraded',
30
+ 'machine.op.failed', 'machine.link.lost', 'session.event.envelope_omitted'
31
+ )
32
+ AND duplicate_of_event_id IS NULL
33
+ AND (turn_association IS NULL OR turn_association = 'current')
34
+ AND (type <> 'agent.message.completed' OR coalesce(payload ->> 'text', '') <> '')
35
+ AND (type <> 'turn.completed' OR (
36
+ NOT (payload ?| array['maintenance', 'segmentLimit'])
37
+ AND coalesce(nullif(payload -> 'output', 'null'::jsonb), payload -> 'result') IS NOT NULL
38
+ AND coalesce(nullif(payload -> 'output', 'null'::jsonb), payload -> 'result') NOT IN ('null'::jsonb, '""'::jsonb)
39
+ ));
40
+
41
+ -- Historical attention_version does not distinguish mark-unread from mark-read
42
+ -- or follow-up intent. Protect human-touched rows only if meaningful content is
43
+ -- still unread (or the explicit empty-session sentinel is present). Reading a
44
+ -- final followed by cleanup must NOT acquire a fabricated manual-unread fence.
45
+ -- The raw migration frontier conservatively dates ambiguous surviving intent;
46
+ -- only genuinely newer consumed work or explicit mark-read removes it.
47
+ -- No acknowledged cursor is advanced and no subject is inferred.
48
+ ALTER TABLE session_pins NO FORCE ROW LEVEL SECURITY;
49
+ ALTER TABLE session_event_cursors NO FORCE ROW LEVEL SECURITY;
50
+ ALTER TABLE session_events NO FORCE ROW LEVEL SECURITY;
51
+ UPDATE session_pins personal SET manually_unread_through = cursor.last_sequence
52
+ FROM session_event_cursors cursor
53
+ WHERE cursor.account_id = personal.account_id
54
+ AND cursor.workspace_id = personal.workspace_id
55
+ AND cursor.session_id = personal.session_id
56
+ AND personal.attention_version > 0
57
+ AND (personal.acknowledged_sequence < 0 OR EXISTS (
58
+ SELECT 1 FROM session_events meaningful
59
+ WHERE meaningful.workspace_id = personal.workspace_id
60
+ AND meaningful.session_id = personal.session_id
61
+ AND meaningful.sequence > personal.acknowledged_sequence
62
+ AND meaningful.type IN (
63
+ 'agent.message.completed', 'turn.completed', 'turn.failed',
64
+ 'session.requiresAction', 'session.humanInput.requested',
65
+ 'tool.auth_needed', 'credential.auth_needed', 'goal.completed', 'goal.paused',
66
+ 'goal.progress', 'goal.rewrite.proposed', 'rig.setup.failed',
67
+ 'sandbox.operation.failed', 'sandbox.box.lost', 'workspace.revision.degraded',
68
+ 'machine.op.failed', 'machine.link.lost', 'session.event.envelope_omitted'
69
+ )
70
+ AND meaningful.duplicate_of_event_id IS NULL
71
+ AND (meaningful.turn_association IS NULL OR meaningful.turn_association = 'current')
72
+ AND (meaningful.type <> 'agent.message.completed' OR coalesce(meaningful.payload ->> 'text', '') <> '')
73
+ AND (meaningful.type <> 'turn.completed' OR (
74
+ NOT (meaningful.payload ?| array['maintenance', 'segmentLimit'])
75
+ AND coalesce(nullif(meaningful.payload -> 'output', 'null'::jsonb), meaningful.payload -> 'result') IS NOT NULL
76
+ AND coalesce(nullif(meaningful.payload -> 'output', 'null'::jsonb), meaningful.payload -> 'result') NOT IN ('null'::jsonb, '""'::jsonb)
77
+ ))
78
+ ));
79
+ ALTER TABLE session_events FORCE ROW LEVEL SECURITY;
80
+ ALTER TABLE session_event_cursors FORCE ROW LEVEL SECURITY;
81
+ ALTER TABLE session_pins FORCE ROW LEVEL SECURITY;
@@ -0,0 +1,26 @@
1
+ -- deployment-mode: rolling
2
+ -- Widen admission only; preserve immutable content and all path/authority checks.
3
+ SET LOCAL lock_timeout = '5s';
4
+ SET LOCAL statement_timeout = '1min';
5
+ DO $limits$
6
+ DECLARE definition text; pair text[];
7
+ BEGIN
8
+ definition := pg_get_functiondef('skill_files_valid(jsonb)'::regprocedure);
9
+ FOREACH pair SLICE 1 IN ARRAY ARRAY[
10
+ ['BETWEEN 1 AND 128','BETWEEN 1 AND 1024'],
11
+ ['bytes > 262144','bytes > 2097152'],
12
+ ['total <= 1048576','total <= 8388608']
13
+ ] LOOP
14
+ IF (length(definition)-length(replace(definition,pair[1],'')))/length(pair[1])<>1 THEN
15
+ RAISE EXCEPTION 'Skill limit validator anchor mismatch: %',pair[1];
16
+ END IF;
17
+ definition:=replace(definition,pair[1],pair[2]);
18
+ END LOOP;
19
+ EXECUTE definition;
20
+ END $limits$;
21
+ ALTER TABLE preference_registry_revisions DROP CONSTRAINT preference_registry_revisions_text_chk;
22
+ ALTER TABLE preference_registry_revisions ADD CONSTRAINT preference_registry_revisions_text_chk CHECK (
23
+ length(btrim(title)) BETWEEN 1 AND 120
24
+ AND length(btrim(description)) BETWEEN 1 AND 1024
25
+ AND length(btrim(content)) > 0 AND length(content) <= 2097152
26
+ );
@@ -0,0 +1,37 @@
1
+ -- deployment-mode: rolling
2
+ -- Repair a regressed control head without replaying historical pause/resume
3
+ -- actions. Revisions are cursors: advancing the head changes no control state.
4
+ SET LOCAL lock_timeout = '5s';
5
+ SET LOCAL statement_timeout = '1min';
6
+
7
+ -- Lock the control head before the event ledger, matching control writers.
8
+ ALTER TABLE workspace_inference_controls NO FORCE ROW LEVEL SECURITY;
9
+ ALTER TABLE workspace_control_events NO FORCE ROW LEVEL SECURITY;
10
+
11
+ UPDATE workspace_inference_controls AS control
12
+ SET revision = frontier.revision
13
+ FROM (
14
+ SELECT workspace_id, max(revision) AS revision
15
+ FROM workspace_control_events
16
+ GROUP BY workspace_id
17
+ ) AS frontier
18
+ WHERE control.workspace_id = frontier.workspace_id
19
+ AND control.revision < frontier.revision;
20
+
21
+ CREATE FUNCTION opengeni_private.prevent_workspace_control_revision_rollback()
22
+ RETURNS trigger LANGUAGE plpgsql AS $$
23
+ BEGIN
24
+ IF NEW.revision < OLD.revision THEN
25
+ RAISE EXCEPTION 'workspace control revision cannot move backwards'
26
+ USING ERRCODE = '23514';
27
+ END IF;
28
+ RETURN NEW;
29
+ END;
30
+ $$;
31
+ REVOKE ALL ON FUNCTION opengeni_private.prevent_workspace_control_revision_rollback() FROM PUBLIC;
32
+ CREATE TRIGGER workspace_control_revision_monotonic
33
+ BEFORE UPDATE OF revision ON workspace_inference_controls
34
+ FOR EACH ROW EXECUTE FUNCTION opengeni_private.prevent_workspace_control_revision_rollback();
35
+
36
+ ALTER TABLE workspace_control_events FORCE ROW LEVEL SECURITY;
37
+ ALTER TABLE workspace_inference_controls FORCE ROW LEVEL SECURITY;
@@ -0,0 +1,3 @@
1
+ -- deployment-mode: rolling
2
+ -- Existing rows retain their preview; new registrations preserve original text.
3
+ ALTER TABLE session_background_commands ADD COLUMN command_text text;
@@ -0,0 +1,27 @@
1
+ -- deployment-mode: rolling
2
+ -- Keep MCP OAuth callback context server-side; provider-facing state is a short reference.
3
+ CREATE TABLE integration_oauth_pending_states (
4
+ id uuid PRIMARY KEY,
5
+ account_id uuid NOT NULL REFERENCES managed_accounts(id) ON DELETE CASCADE,
6
+ workspace_id uuid NOT NULL,
7
+ state_encrypted text NOT NULL CHECK (octet_length(state_encrypted) BETWEEN 1 AND 32768),
8
+ expires_at timestamptz NOT NULL,
9
+ created_at timestamptz NOT NULL DEFAULT now(),
10
+ CONSTRAINT integration_oauth_pending_states_workspace_account_fk
11
+ FOREIGN KEY (workspace_id, account_id) REFERENCES workspaces(id, account_id) ON DELETE CASCADE
12
+ );
13
+ CREATE INDEX integration_oauth_pending_states_expiry_idx
14
+ ON integration_oauth_pending_states (workspace_id, expires_at);
15
+ ALTER TABLE integration_oauth_pending_states ENABLE ROW LEVEL SECURITY;
16
+ ALTER TABLE integration_oauth_pending_states FORCE ROW LEVEL SECURITY;
17
+ CREATE POLICY integration_oauth_pending_states_workspace_scope
18
+ ON integration_oauth_pending_states
19
+ USING (opengeni_private.workspace_rls_visible(account_id, workspace_id))
20
+ WITH CHECK (opengeni_private.workspace_rls_visible(account_id, workspace_id));
21
+ REVOKE ALL ON TABLE integration_oauth_pending_states FROM PUBLIC;
22
+ DO $$
23
+ BEGIN
24
+ IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
25
+ GRANT SELECT, INSERT, UPDATE, DELETE ON integration_oauth_pending_states TO opengeni_app;
26
+ END IF;
27
+ END $$;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/db",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -114,10 +114,10 @@
114
114
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
115
115
  },
116
116
  "dependencies": {
117
- "@opengeni/codemode": "^0.6.0",
118
- "@opengeni/codex": "^0.2.24",
119
- "@opengeni/config": "^2.0.0",
120
- "@opengeni/contracts": "^5.0.0",
117
+ "@opengeni/codemode": "^0.6.1",
118
+ "@opengeni/codex": "^0.2.25",
119
+ "@opengeni/config": "^2.1.0",
120
+ "@opengeni/contracts": "^5.1.0",
121
121
  "@opengeni/network": "^0.3.1",
122
122
  "drizzle-orm": "^0.45.2",
123
123
  "postgres": "^3.4.7"
@@ -0,0 +1,170 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { fromPostgresLosslessJson } from "./lossless-json";
3
+
4
+ /** Decode a stored row exactly once, by its own out-of-band version. Removing
5
+ * the version at this boundary prevents literal marker text being decoded again
6
+ * after the logical payload is placed inside a separately versioned receipt. */
7
+ export function logicalChildReadEvent<
8
+ T extends { payload: unknown; payloadCodecVersion: number | null },
9
+ >(row: T): Omit<T, "payloadCodecVersion"> {
10
+ const { payloadCodecVersion, ...event } = row;
11
+ return { ...event, payload: fromPostgresLosslessJson(row.payload, payloadCodecVersion) };
12
+ }
13
+
14
+ /** Rows arrive newest first from the bounded indexed query. Budget what the
15
+ * parent actually reads, not the PostgreSQL string compatibility encoding. */
16
+ export function boundedChildLifecycleEvidence(
17
+ rows: readonly {
18
+ sequence: number;
19
+ type: string;
20
+ payload: unknown;
21
+ payloadCodecVersion: number | null;
22
+ }[],
23
+ ): Array<{ sequence: number; type: string; payload: unknown }> {
24
+ const evidence: Array<{ sequence: number; type: string; payload: unknown }> = [];
25
+ let bytes = 0;
26
+ for (const row of rows.slice(0, 32)) {
27
+ const { sequence, type, payload } = logicalChildReadEvent(row);
28
+ const candidate = { sequence, type, payload };
29
+ const size = Buffer.byteLength(JSON.stringify(candidate), "utf8");
30
+ if (bytes + size > 8192) continue;
31
+ evidence.push(candidate);
32
+ bytes += size;
33
+ }
34
+ return evidence.reverse();
35
+ }
36
+
37
+ type RecordValue = Record<string, unknown>;
38
+ const record = (value: unknown): RecordValue | null =>
39
+ value !== null && typeof value === "object" && !Array.isArray(value)
40
+ ? (value as RecordValue)
41
+ : null;
42
+
43
+ /** Historical tool output is a potentially lossy audit lane. Accept only known
44
+ * complete envelopes, then compare each returned item with its retained source.
45
+ * Tool names, cursors and terminal status are never content evidence alone. */
46
+ export function historicalChildReadItems(
47
+ name: unknown,
48
+ argsValue: unknown,
49
+ outputValue: unknown,
50
+ ): Array<{ sessionId: string; item: RecordValue; view: string }> {
51
+ const decode = (value: unknown): unknown => {
52
+ if (typeof value !== "string") return value;
53
+ if (Buffer.byteLength(value) > 256 * 1024) return null;
54
+ try {
55
+ return JSON.parse(value);
56
+ } catch {
57
+ return null;
58
+ }
59
+ };
60
+ const args = record(decode(argsValue));
61
+ let output = decode(outputValue);
62
+ const envelope = record(output);
63
+ if (envelope?.isError === true) return [];
64
+ const content = envelope?.content ?? (Array.isArray(output) ? output : null);
65
+ if (content !== null) {
66
+ if (!Array.isArray(content) || content.length !== 1) return [];
67
+ const text = record(content[0]);
68
+ if (text?.type !== "text") return [];
69
+ output = decode(text.text);
70
+ }
71
+ const page = record(output);
72
+ if (
73
+ !args ||
74
+ !page ||
75
+ page.truncated === true ||
76
+ record(page.truncation)?.truncated === true ||
77
+ page.sourceExact === false
78
+ )
79
+ return [];
80
+ if (name === "opengeni__session_wait") {
81
+ if (page.aborted === true || !Array.isArray(page.changed) || !Array.isArray(args.targets))
82
+ return [];
83
+ const targets = new Set(args.targets.map((target) => record(target)?.sessionId));
84
+ return page.changed.flatMap((value) => {
85
+ const target = record(value);
86
+ if (
87
+ !target ||
88
+ typeof target.sessionId !== "string" ||
89
+ !targets.has(target.sessionId) ||
90
+ !Array.isArray(target.events)
91
+ )
92
+ return [];
93
+ return target.events.flatMap((eventValue) => {
94
+ const item = record(eventValue);
95
+ return item ? [{ sessionId: target.sessionId as string, item, view: "wait" }] : [];
96
+ });
97
+ });
98
+ }
99
+ if (name !== "opengeni__session_events" || typeof args.sessionId !== "string") return [];
100
+ if (Array.isArray(page.events)) {
101
+ const view =
102
+ page.view === "conversation" || page.view === "results"
103
+ ? page.view
104
+ : page.payloadMode === "full"
105
+ ? "debug"
106
+ : null;
107
+ if (!view || (view !== "debug" && page.sourceExact !== true)) return [];
108
+ return page.events.flatMap((value) => {
109
+ const item = record(value);
110
+ const fragment = record(item?.fragment);
111
+ if (
112
+ !item ||
113
+ item.sourceOmitted ||
114
+ (item.fragment !== undefined &&
115
+ (!fragment || fragment.offset !== 0 || fragment.complete !== true))
116
+ )
117
+ return [];
118
+ return [{ sessionId: args.sessionId as string, item, view }];
119
+ });
120
+ }
121
+ if (page.version === 1 && record(page.truncation)?.truncated === false) {
122
+ return [{ sessionId: args.sessionId, item: page, view: "compact" }];
123
+ }
124
+ return [];
125
+ }
126
+
127
+ export function historicalChildReadMatches(
128
+ candidate: ReturnType<typeof historicalChildReadItems>[number],
129
+ event: { id: string; sequence: number; type: string; payload: unknown },
130
+ ): boolean {
131
+ const { item, view } = candidate;
132
+ const retained = record(event.payload);
133
+ if (record(retained?.truncation)?.truncated === true || retained?.sourceOmitted === true)
134
+ return false;
135
+ if (
136
+ item.sequence !== event.sequence ||
137
+ (item.id !== undefined && item.id !== event.id) ||
138
+ (item.type !== undefined && item.type !== event.type)
139
+ )
140
+ return false;
141
+ if (view === "debug") return isDeepStrictEqual(item.payload, event.payload);
142
+ const payload = record(event.payload);
143
+ if (!payload) return false;
144
+ if (view === "conversation") {
145
+ return (
146
+ event.type === "agent.message.completed" &&
147
+ typeof payload.text === "string" &&
148
+ item.text === payload.text
149
+ );
150
+ }
151
+ if (view === "results") {
152
+ const value = event.type === "turn.completed" ? (payload.output ?? payload.result) : payload;
153
+ return (
154
+ value !== undefined &&
155
+ item.text === (typeof value === "string" ? value : JSON.stringify(value))
156
+ );
157
+ }
158
+ // Compact/wait summaries cannot prove that omitted questions, errors or goal
159
+ // fields were consumed. Restrict historical reconciliation to exact answers.
160
+ if (event.type !== "turn.completed" && event.type !== "agent.message.completed") return false;
161
+ const answer =
162
+ event.type === "turn.completed" ? (payload.output ?? payload.result) : payload.text;
163
+ return (
164
+ typeof answer === "string" &&
165
+ answer.length > 0 &&
166
+ (item.text === answer ||
167
+ item.result === answer ||
168
+ (view === "compact" && item.output === answer))
169
+ );
170
+ }