@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.
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-reconciliation.js +2 -2
- package/dist/child-read-reconciliation.d.ts +35 -0
- package/dist/{chunk-CJI6G254.js → chunk-4CCFFLDZ.js} +2 -2
- package/dist/{chunk-7JCDSMX2.js → chunk-4RDUZ5UV.js} +2 -2
- package/dist/{chunk-SYKHLPTK.js → chunk-6ORJPCYW.js} +2 -2
- package/dist/{chunk-GABXCG3N.js → chunk-CSVHCF3R.js} +11 -6
- package/dist/chunk-CSVHCF3R.js.map +1 -0
- package/dist/{chunk-CDAT6EKW.js → chunk-FULLE5CF.js} +3 -3
- package/dist/{chunk-MN5PZMEK.js → chunk-GSB4ISHE.js} +905 -801
- package/dist/chunk-GSB4ISHE.js.map +1 -0
- package/dist/{chunk-XIXVVBW2.js → chunk-H7BC444C.js} +3 -1
- package/dist/chunk-H7BC444C.js.map +1 -0
- package/dist/{chunk-LVY33EWL.js → chunk-I7HQL6FK.js} +3 -3
- package/dist/{chunk-RIYHP2WU.js → chunk-JILAWE26.js} +2 -2
- package/dist/{chunk-ZPJ6V2VG.js → chunk-LO6BVXXH.js} +2 -2
- package/dist/{chunk-PJI4MUZ7.js → chunk-LSLNMENV.js} +4 -4
- package/dist/{chunk-RL2MUXM3.js → chunk-PGIFHEBA.js} +2 -2
- package/dist/{chunk-G32NVMRJ.js → chunk-RXH5CFG3.js} +5 -5
- package/dist/{chunk-AJA2ZIUW.js → chunk-XK6HXON2.js} +2 -2
- package/dist/connect-attempts.js +3 -3
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/index.d.ts +31 -0
- package/dist/index.js +6651 -6244
- package/dist/index.js.map +1 -1
- package/dist/integration-oauth-pending-states.d.ts +14 -0
- package/dist/managed-auth-session-sets.js +3 -3
- package/dist/mcp-operations.js +2 -2
- package/dist/new-session-drafts.d.ts +2 -1
- package/dist/organization-integration-policy.js +3 -3
- package/dist/provision-roles.js +1 -1
- package/dist/retained-provider-commands.js +6 -6
- package/dist/runtime-posture.d.ts +2 -2
- package/dist/schema.d.ts +151 -8
- package/dist/schema.js +3 -1
- package/dist/session-background-commands.js +4 -4
- package/dist/session-command-output.js +5 -5
- package/dist/session-event-slices.js +3 -3
- package/dist/session-mcp-credential-rotation.js +4 -4
- package/dist/session-meaningful-events.d.ts +15 -0
- package/dist/session-tenancy.js +3 -3
- package/dist/skills.d.ts +2 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-tool-defaults.js +4 -4
- package/drizzle/0503_session_meaningful_attention.sql +81 -0
- package/drizzle/0504_skill_folder_limits.sql +26 -0
- package/drizzle/0505_workspace_control_revision_frontier.sql +37 -0
- package/drizzle/0506_background_command_text.sql +3 -0
- package/drizzle/0507_integration_oauth_pending_states.sql +27 -0
- package/package.json +5 -5
- package/src/child-read-reconciliation.ts +170 -0
- package/src/index.ts +439 -60
- package/src/integration-oauth-pending-states.ts +52 -0
- package/src/new-session-drafts.ts +4 -2
- package/src/runtime-posture.ts +2 -0
- package/src/schema.ts +35 -3
- package/src/session-background-commands.ts +8 -2
- package/src/session-meaningful-events.ts +97 -0
- package/src/skills.ts +14 -1
- package/dist/chunk-GABXCG3N.js.map +0 -1
- package/dist/chunk-MN5PZMEK.js.map +0 -1
- package/dist/chunk-XIXVVBW2.js.map +0 -1
- /package/dist/{chunk-CJI6G254.js.map → chunk-4CCFFLDZ.js.map} +0 -0
- /package/dist/{chunk-7JCDSMX2.js.map → chunk-4RDUZ5UV.js.map} +0 -0
- /package/dist/{chunk-SYKHLPTK.js.map → chunk-6ORJPCYW.js.map} +0 -0
- /package/dist/{chunk-CDAT6EKW.js.map → chunk-FULLE5CF.js.map} +0 -0
- /package/dist/{chunk-LVY33EWL.js.map → chunk-I7HQL6FK.js.map} +0 -0
- /package/dist/{chunk-RIYHP2WU.js.map → chunk-JILAWE26.js.map} +0 -0
- /package/dist/{chunk-ZPJ6V2VG.js.map → chunk-LO6BVXXH.js.map} +0 -0
- /package/dist/{chunk-PJI4MUZ7.js.map → chunk-LSLNMENV.js.map} +0 -0
- /package/dist/{chunk-RL2MUXM3.js.map → chunk-PGIFHEBA.js.map} +0 -0
- /package/dist/{chunk-G32NVMRJ.js.map → chunk-RXH5CFG3.js.map} +0 -0
- /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,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.
|
|
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.
|
|
118
|
-
"@opengeni/codex": "^0.2.
|
|
119
|
-
"@opengeni/config": "^2.
|
|
120
|
-
"@opengeni/contracts": "^5.
|
|
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
|
+
}
|