@opengeni/db 1.0.1 → 3.2.0-canary.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/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1606 -81
- package/dist/index.js +21898 -8147
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +29 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +43 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +268 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +28 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11676 -1550
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +138 -11
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
SET lock_timeout = '5s';
|
|
3
|
+
SET statement_timeout = '10min';
|
|
4
|
+
|
|
5
|
+
-- Widen the task-note expiry ceiling from 30 to 90 days. Task notes are pure
|
|
6
|
+
-- agent-to-agent coordination within one root session tree; a resumed root
|
|
7
|
+
-- session/task tree after a long pause otherwise silently loses all
|
|
8
|
+
-- coordination notes with no warning, since expiry is a read-time filter, not
|
|
9
|
+
-- a deletion. No documented rationale exists for the original 30-day figure.
|
|
10
|
+
-- This is fully backward compatible: every existing row and every caller
|
|
11
|
+
-- still supplying 1-30 days keeps working unchanged.
|
|
12
|
+
|
|
13
|
+
ALTER TABLE "task_notes"
|
|
14
|
+
DROP CONSTRAINT "task_notes_expiry_check";
|
|
15
|
+
ALTER TABLE "task_notes"
|
|
16
|
+
ADD CONSTRAINT "task_notes_expiry_check"
|
|
17
|
+
CHECK ("expires_at" > "created_at" AND "expires_at" <= "created_at" + interval '90 days')
|
|
18
|
+
NOT VALID;
|
|
19
|
+
ALTER TABLE "task_notes"
|
|
20
|
+
VALIDATE CONSTRAINT "task_notes_expiry_check";
|
|
21
|
+
|
|
22
|
+
-- Function bodies below are byte-identical to their prior definitions
|
|
23
|
+
-- (0239_task_tree_notes.sql / 0260_task_note_knowledge_promotion.sql) except
|
|
24
|
+
-- the one numeric bound each enforces in its input-validation check.
|
|
25
|
+
|
|
26
|
+
CREATE OR REPLACE FUNCTION create_task_note_for_attempt(
|
|
27
|
+
p_account_id uuid,
|
|
28
|
+
p_workspace_id uuid,
|
|
29
|
+
p_session_id uuid,
|
|
30
|
+
p_turn_id uuid,
|
|
31
|
+
p_attempt_id uuid,
|
|
32
|
+
p_execution_generation integer,
|
|
33
|
+
p_operation_id uuid,
|
|
34
|
+
p_kind text,
|
|
35
|
+
p_text text,
|
|
36
|
+
p_expires_in_days integer
|
|
37
|
+
) RETURNS TABLE (
|
|
38
|
+
note_id uuid, root_session_id uuid, kind text, note_text text, status text,
|
|
39
|
+
version integer, expires_at timestamptz, created_at timestamptz,
|
|
40
|
+
updated_at timestamptz, archived_at timestamptz, actor_kind text,
|
|
41
|
+
source_session_id uuid, source_turn_id uuid, replayed boolean
|
|
42
|
+
)
|
|
43
|
+
LANGUAGE plpgsql
|
|
44
|
+
SECURITY DEFINER
|
|
45
|
+
SET search_path FROM CURRENT
|
|
46
|
+
AS $$
|
|
47
|
+
DECLARE
|
|
48
|
+
authority record;
|
|
49
|
+
existing_event task_note_events%ROWTYPE;
|
|
50
|
+
note_row task_notes%ROWTYPE;
|
|
51
|
+
calculated_text_hash text;
|
|
52
|
+
calculated_input_hash text;
|
|
53
|
+
calculated_expires_at timestamptz;
|
|
54
|
+
write_capability_id uuid := gen_random_uuid();
|
|
55
|
+
previous_capability text := current_setting('opengeni.task_note_write_capability', true);
|
|
56
|
+
BEGIN
|
|
57
|
+
SELECT * INTO STRICT authority FROM resolve_task_note_attempt_authority(
|
|
58
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
59
|
+
p_attempt_id, p_execution_generation
|
|
60
|
+
);
|
|
61
|
+
IF p_operation_id IS NULL
|
|
62
|
+
OR p_kind NOT IN ('finding','decision','blocker','ownership','artifact','handoff')
|
|
63
|
+
OR p_text IS NULL OR octet_length(p_text) NOT BETWEEN 1 AND 4096
|
|
64
|
+
OR p_text <> btrim(p_text)
|
|
65
|
+
OR p_expires_in_days IS NULL OR p_expires_in_days NOT BETWEEN 1 AND 90
|
|
66
|
+
THEN
|
|
67
|
+
RAISE EXCEPTION 'task note input is invalid' USING ERRCODE = '22023';
|
|
68
|
+
END IF;
|
|
69
|
+
calculated_expires_at := transaction_timestamp()
|
|
70
|
+
+ pg_catalog.make_interval(days => p_expires_in_days);
|
|
71
|
+
calculated_text_hash := encode(sha256(convert_to(p_text, 'UTF8')), 'hex');
|
|
72
|
+
calculated_input_hash := encode(sha256(convert_to(jsonb_build_object(
|
|
73
|
+
'accountId', p_account_id, 'workspaceId', p_workspace_id,
|
|
74
|
+
'rootSessionId', authority.root_session_id, 'sessionId', p_session_id,
|
|
75
|
+
'turnId', p_turn_id, 'attemptId', p_attempt_id,
|
|
76
|
+
'executionGeneration', p_execution_generation, 'kind', p_kind,
|
|
77
|
+
'textHash', calculated_text_hash, 'expiresInDays', p_expires_in_days
|
|
78
|
+
)::text, 'UTF8')), 'hex');
|
|
79
|
+
|
|
80
|
+
INSERT INTO task_note_write_capabilities (backend_pid, transaction_id, capability_id)
|
|
81
|
+
VALUES (pg_backend_pid(), pg_current_xact_id(), write_capability_id);
|
|
82
|
+
PERFORM set_config('opengeni.task_note_write_capability', write_capability_id::text, true);
|
|
83
|
+
|
|
84
|
+
SELECT * INTO existing_event FROM task_note_events event
|
|
85
|
+
WHERE event.workspace_id = p_workspace_id AND event.operation_id = p_operation_id
|
|
86
|
+
FOR SHARE;
|
|
87
|
+
IF FOUND THEN
|
|
88
|
+
IF existing_event.event_type <> 'created'
|
|
89
|
+
OR existing_event.input_hash <> calculated_input_hash
|
|
90
|
+
OR existing_event.root_session_id <> authority.root_session_id
|
|
91
|
+
OR existing_event.actor_session_id <> p_session_id
|
|
92
|
+
OR existing_event.actor_turn_id <> p_turn_id
|
|
93
|
+
OR existing_event.actor_attempt_id <> p_attempt_id
|
|
94
|
+
OR existing_event.actor_execution_generation <> p_execution_generation
|
|
95
|
+
THEN
|
|
96
|
+
RAISE EXCEPTION 'task note operation conflicts with another input or attempt'
|
|
97
|
+
USING ERRCODE = '23505';
|
|
98
|
+
END IF;
|
|
99
|
+
SELECT * INTO STRICT note_row FROM task_notes note
|
|
100
|
+
WHERE note.workspace_id = p_workspace_id AND note.id = existing_event.note_id;
|
|
101
|
+
replayed := true;
|
|
102
|
+
ELSE
|
|
103
|
+
IF (SELECT count(*) FROM task_notes note
|
|
104
|
+
WHERE note.workspace_id = p_workspace_id
|
|
105
|
+
AND note.root_session_id = authority.root_session_id
|
|
106
|
+
AND note.status = 'active' AND note.expires_at > transaction_timestamp()) >= 500
|
|
107
|
+
THEN
|
|
108
|
+
RAISE EXCEPTION 'task note active-record limit reached' USING ERRCODE = '54000';
|
|
109
|
+
END IF;
|
|
110
|
+
INSERT INTO task_notes (
|
|
111
|
+
account_id, workspace_id, root_session_id, kind, text, text_hash,
|
|
112
|
+
expires_at, create_operation_id, create_input_hash,
|
|
113
|
+
created_by_actor_kind, created_by_actor_subject_id,
|
|
114
|
+
created_by_initiating_human_subject_id, created_by_session_id,
|
|
115
|
+
created_by_turn_id, created_by_attempt_id, created_by_execution_generation
|
|
116
|
+
) VALUES (
|
|
117
|
+
p_account_id, p_workspace_id, authority.root_session_id, p_kind, p_text,
|
|
118
|
+
calculated_text_hash, calculated_expires_at, p_operation_id, calculated_input_hash,
|
|
119
|
+
authority.actor_kind, authority.actor_subject_id,
|
|
120
|
+
authority.initiating_human_subject_id, p_session_id, p_turn_id,
|
|
121
|
+
p_attempt_id, p_execution_generation
|
|
122
|
+
) RETURNING * INTO note_row;
|
|
123
|
+
INSERT INTO task_note_events (
|
|
124
|
+
account_id, workspace_id, root_session_id, note_id, event_type,
|
|
125
|
+
note_version, operation_id, input_hash, text_hash, actor_kind,
|
|
126
|
+
actor_subject_id, initiating_human_subject_id, actor_session_id,
|
|
127
|
+
actor_turn_id, actor_attempt_id, actor_execution_generation
|
|
128
|
+
) VALUES (
|
|
129
|
+
p_account_id, p_workspace_id, authority.root_session_id, note_row.id,
|
|
130
|
+
'created', 1, p_operation_id, calculated_input_hash,
|
|
131
|
+
calculated_text_hash, authority.actor_kind, authority.actor_subject_id,
|
|
132
|
+
authority.initiating_human_subject_id, p_session_id, p_turn_id,
|
|
133
|
+
p_attempt_id, p_execution_generation
|
|
134
|
+
);
|
|
135
|
+
replayed := false;
|
|
136
|
+
END IF;
|
|
137
|
+
|
|
138
|
+
note_id := note_row.id; root_session_id := note_row.root_session_id;
|
|
139
|
+
kind := note_row.kind; note_text := note_row.text; status := note_row.status;
|
|
140
|
+
version := note_row.version; expires_at := note_row.expires_at;
|
|
141
|
+
created_at := note_row.created_at; updated_at := note_row.updated_at;
|
|
142
|
+
archived_at := note_row.archived_at; actor_kind := note_row.created_by_actor_kind;
|
|
143
|
+
source_session_id := note_row.created_by_session_id;
|
|
144
|
+
source_turn_id := note_row.created_by_turn_id;
|
|
145
|
+
DELETE FROM task_note_write_capabilities capability
|
|
146
|
+
WHERE capability.backend_pid = pg_backend_pid()
|
|
147
|
+
AND capability.transaction_id = pg_current_xact_id()
|
|
148
|
+
AND capability.capability_id = write_capability_id;
|
|
149
|
+
PERFORM set_config('opengeni.task_note_write_capability', coalesce(previous_capability, ''), true);
|
|
150
|
+
RETURN NEXT;
|
|
151
|
+
EXCEPTION WHEN OTHERS THEN
|
|
152
|
+
PERFORM set_config('opengeni.task_note_write_capability', coalesce(previous_capability, ''), true);
|
|
153
|
+
RAISE;
|
|
154
|
+
END;
|
|
155
|
+
$$;
|
|
156
|
+
|
|
157
|
+
CREATE OR REPLACE FUNCTION replace_task_note_for_attempt(
|
|
158
|
+
p_account_id uuid,
|
|
159
|
+
p_workspace_id uuid,
|
|
160
|
+
p_session_id uuid,
|
|
161
|
+
p_turn_id uuid,
|
|
162
|
+
p_attempt_id uuid,
|
|
163
|
+
p_execution_generation integer,
|
|
164
|
+
p_operation_id uuid,
|
|
165
|
+
p_archive_operation_id uuid,
|
|
166
|
+
p_create_operation_id uuid,
|
|
167
|
+
p_replaced_note_id uuid,
|
|
168
|
+
p_expected_replaced_version integer,
|
|
169
|
+
p_replacement_kind text,
|
|
170
|
+
p_replacement_text text,
|
|
171
|
+
p_replacement_expires_in_days integer,
|
|
172
|
+
p_reason text
|
|
173
|
+
) RETURNS TABLE (
|
|
174
|
+
operation_id uuid,
|
|
175
|
+
input_hash text,
|
|
176
|
+
replaced_note_id uuid,
|
|
177
|
+
replaced_note_version integer,
|
|
178
|
+
replacement_note_id uuid,
|
|
179
|
+
root_session_id uuid,
|
|
180
|
+
replacement_kind text,
|
|
181
|
+
replacement_text text,
|
|
182
|
+
replacement_status text,
|
|
183
|
+
replacement_version integer,
|
|
184
|
+
replacement_expires_at timestamptz,
|
|
185
|
+
replacement_created_at timestamptz,
|
|
186
|
+
replacement_updated_at timestamptz,
|
|
187
|
+
replacement_archived_at timestamptz,
|
|
188
|
+
replacement_actor_kind text,
|
|
189
|
+
replacement_source_session_id uuid,
|
|
190
|
+
replacement_source_turn_id uuid,
|
|
191
|
+
replayed boolean
|
|
192
|
+
)
|
|
193
|
+
LANGUAGE plpgsql
|
|
194
|
+
SECURITY DEFINER
|
|
195
|
+
SET search_path FROM CURRENT
|
|
196
|
+
AS $$
|
|
197
|
+
DECLARE
|
|
198
|
+
authority record;
|
|
199
|
+
receipt_row task_note_replacement_receipts%ROWTYPE;
|
|
200
|
+
replaced_row task_notes%ROWTYPE;
|
|
201
|
+
replacement_row task_notes%ROWTYPE;
|
|
202
|
+
archived_result record;
|
|
203
|
+
created_result record;
|
|
204
|
+
calculated_replacement_text_hash text;
|
|
205
|
+
calculated_input_hash text;
|
|
206
|
+
BEGIN
|
|
207
|
+
SELECT * INTO STRICT authority FROM resolve_task_note_attempt_authority(
|
|
208
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
209
|
+
p_attempt_id, p_execution_generation
|
|
210
|
+
);
|
|
211
|
+
IF p_operation_id IS NULL
|
|
212
|
+
OR p_archive_operation_id IS NULL
|
|
213
|
+
OR p_create_operation_id IS NULL
|
|
214
|
+
OR p_operation_id = p_archive_operation_id
|
|
215
|
+
OR p_operation_id = p_create_operation_id
|
|
216
|
+
OR p_archive_operation_id = p_create_operation_id
|
|
217
|
+
OR p_replaced_note_id IS NULL
|
|
218
|
+
OR p_expected_replaced_version IS DISTINCT FROM 1
|
|
219
|
+
OR p_replacement_kind NOT IN ('finding','decision','blocker','ownership','artifact','handoff')
|
|
220
|
+
OR p_replacement_text IS NULL
|
|
221
|
+
OR octet_length(p_replacement_text) NOT BETWEEN 1 AND 4096
|
|
222
|
+
OR p_replacement_text <> btrim(p_replacement_text)
|
|
223
|
+
OR p_replacement_expires_in_days IS NULL
|
|
224
|
+
OR p_replacement_expires_in_days NOT BETWEEN 1 AND 90
|
|
225
|
+
OR p_reason IS NULL
|
|
226
|
+
OR octet_length(p_reason) NOT BETWEEN 1 AND 2048
|
|
227
|
+
OR p_reason <> btrim(p_reason)
|
|
228
|
+
THEN
|
|
229
|
+
RAISE EXCEPTION 'Task-note replacement input is invalid' USING ERRCODE = '22023';
|
|
230
|
+
END IF;
|
|
231
|
+
|
|
232
|
+
calculated_replacement_text_hash := encode(
|
|
233
|
+
sha256(convert_to(p_replacement_text, 'UTF8')), 'hex'
|
|
234
|
+
);
|
|
235
|
+
calculated_input_hash := encode(sha256(convert_to(jsonb_build_object(
|
|
236
|
+
'accountId', p_account_id,
|
|
237
|
+
'workspaceId', p_workspace_id,
|
|
238
|
+
'rootSessionId', authority.root_session_id,
|
|
239
|
+
'sessionId', p_session_id,
|
|
240
|
+
'turnId', p_turn_id,
|
|
241
|
+
'attemptId', p_attempt_id,
|
|
242
|
+
'executionGeneration', p_execution_generation,
|
|
243
|
+
'operationId', p_operation_id,
|
|
244
|
+
'archiveOperationId', p_archive_operation_id,
|
|
245
|
+
'createOperationId', p_create_operation_id,
|
|
246
|
+
'replacedNoteId', p_replaced_note_id,
|
|
247
|
+
'expectedReplacedVersion', p_expected_replaced_version,
|
|
248
|
+
'replacementKind', p_replacement_kind,
|
|
249
|
+
'replacementTextHash', calculated_replacement_text_hash,
|
|
250
|
+
'replacementExpiresInDays', p_replacement_expires_in_days,
|
|
251
|
+
'reason', p_reason
|
|
252
|
+
)::text, 'UTF8')), 'hex');
|
|
253
|
+
|
|
254
|
+
SELECT receipt.* INTO receipt_row
|
|
255
|
+
FROM task_note_replacement_receipts receipt
|
|
256
|
+
WHERE receipt.workspace_id = p_workspace_id
|
|
257
|
+
AND receipt.operation_id = p_operation_id
|
|
258
|
+
FOR SHARE;
|
|
259
|
+
IF FOUND THEN
|
|
260
|
+
IF receipt_row.account_id IS DISTINCT FROM p_account_id
|
|
261
|
+
OR receipt_row.root_session_id IS DISTINCT FROM authority.root_session_id
|
|
262
|
+
OR receipt_row.input_hash IS DISTINCT FROM calculated_input_hash
|
|
263
|
+
OR receipt_row.replaced_note_id IS DISTINCT FROM p_replaced_note_id
|
|
264
|
+
OR receipt_row.archive_operation_id IS DISTINCT FROM p_archive_operation_id
|
|
265
|
+
OR receipt_row.create_operation_id IS DISTINCT FROM p_create_operation_id
|
|
266
|
+
OR receipt_row.actor_session_id IS DISTINCT FROM p_session_id
|
|
267
|
+
OR receipt_row.actor_turn_id IS DISTINCT FROM p_turn_id
|
|
268
|
+
OR receipt_row.actor_attempt_id IS DISTINCT FROM p_attempt_id
|
|
269
|
+
OR receipt_row.actor_execution_generation IS DISTINCT FROM p_execution_generation
|
|
270
|
+
OR receipt_row.actor_kind IS DISTINCT FROM authority.actor_kind
|
|
271
|
+
OR receipt_row.actor_subject_id IS DISTINCT FROM authority.actor_subject_id
|
|
272
|
+
OR receipt_row.initiating_human_subject_id
|
|
273
|
+
IS DISTINCT FROM authority.initiating_human_subject_id
|
|
274
|
+
THEN
|
|
275
|
+
RAISE EXCEPTION 'Task-note replacement operation conflicts with another input or attempt'
|
|
276
|
+
USING ERRCODE = '23505';
|
|
277
|
+
END IF;
|
|
278
|
+
SELECT note.* INTO STRICT replacement_row
|
|
279
|
+
FROM task_notes note
|
|
280
|
+
WHERE note.workspace_id = p_workspace_id
|
|
281
|
+
AND note.root_session_id = authority.root_session_id
|
|
282
|
+
AND note.id = receipt_row.replacement_note_id;
|
|
283
|
+
replayed := true;
|
|
284
|
+
ELSE
|
|
285
|
+
SELECT note.* INTO replaced_row
|
|
286
|
+
FROM task_notes note
|
|
287
|
+
WHERE note.workspace_id = p_workspace_id
|
|
288
|
+
AND note.root_session_id = authority.root_session_id
|
|
289
|
+
AND note.id = p_replaced_note_id
|
|
290
|
+
FOR UPDATE;
|
|
291
|
+
IF NOT FOUND THEN
|
|
292
|
+
RAISE EXCEPTION 'Task note is unavailable for replacement' USING ERRCODE = 'P0002';
|
|
293
|
+
END IF;
|
|
294
|
+
IF replaced_row.status <> 'active'
|
|
295
|
+
OR replaced_row.version <> p_expected_replaced_version
|
|
296
|
+
OR replaced_row.expires_at <= transaction_timestamp()
|
|
297
|
+
THEN
|
|
298
|
+
RAISE EXCEPTION 'Task-note replacement version or lifecycle conflict'
|
|
299
|
+
USING ERRCODE = '40001';
|
|
300
|
+
END IF;
|
|
301
|
+
|
|
302
|
+
SELECT * INTO STRICT archived_result FROM archive_task_note_for_attempt(
|
|
303
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
304
|
+
p_attempt_id, p_execution_generation, p_archive_operation_id,
|
|
305
|
+
p_replaced_note_id, p_expected_replaced_version, p_reason
|
|
306
|
+
);
|
|
307
|
+
SELECT * INTO STRICT created_result FROM create_task_note_for_attempt(
|
|
308
|
+
p_account_id, p_workspace_id, p_session_id, p_turn_id,
|
|
309
|
+
p_attempt_id, p_execution_generation, p_create_operation_id,
|
|
310
|
+
p_replacement_kind, p_replacement_text, p_replacement_expires_in_days
|
|
311
|
+
);
|
|
312
|
+
IF archived_result.note_id IS DISTINCT FROM p_replaced_note_id
|
|
313
|
+
OR archived_result.status IS DISTINCT FROM 'archived'
|
|
314
|
+
OR archived_result.version IS DISTINCT FROM 2
|
|
315
|
+
OR created_result.status IS DISTINCT FROM 'active'
|
|
316
|
+
OR created_result.version IS DISTINCT FROM 1
|
|
317
|
+
OR created_result.root_session_id IS DISTINCT FROM authority.root_session_id
|
|
318
|
+
THEN
|
|
319
|
+
RAISE EXCEPTION 'Task-note replacement lifecycle returned an invalid projection'
|
|
320
|
+
USING ERRCODE = '23514';
|
|
321
|
+
END IF;
|
|
322
|
+
SELECT note.* INTO STRICT replacement_row
|
|
323
|
+
FROM task_notes note
|
|
324
|
+
WHERE note.workspace_id = p_workspace_id
|
|
325
|
+
AND note.root_session_id = authority.root_session_id
|
|
326
|
+
AND note.id = created_result.note_id;
|
|
327
|
+
|
|
328
|
+
INSERT INTO task_note_replacement_receipts (
|
|
329
|
+
account_id, workspace_id, root_session_id, operation_id, input_hash,
|
|
330
|
+
replaced_note_id, replaced_note_version, replacement_note_id,
|
|
331
|
+
replacement_note_version, archive_operation_id, create_operation_id,
|
|
332
|
+
actor_kind, actor_subject_id, initiating_human_subject_id,
|
|
333
|
+
actor_session_id, actor_turn_id, actor_attempt_id, actor_execution_generation
|
|
334
|
+
) VALUES (
|
|
335
|
+
p_account_id, p_workspace_id, authority.root_session_id,
|
|
336
|
+
p_operation_id, calculated_input_hash, p_replaced_note_id, 2,
|
|
337
|
+
replacement_row.id, 1, p_archive_operation_id, p_create_operation_id,
|
|
338
|
+
authority.actor_kind, authority.actor_subject_id,
|
|
339
|
+
authority.initiating_human_subject_id, p_session_id, p_turn_id,
|
|
340
|
+
p_attempt_id, p_execution_generation
|
|
341
|
+
) RETURNING * INTO STRICT receipt_row;
|
|
342
|
+
replayed := false;
|
|
343
|
+
END IF;
|
|
344
|
+
|
|
345
|
+
operation_id := receipt_row.operation_id;
|
|
346
|
+
input_hash := receipt_row.input_hash;
|
|
347
|
+
replaced_note_id := receipt_row.replaced_note_id;
|
|
348
|
+
replaced_note_version := receipt_row.replaced_note_version;
|
|
349
|
+
replacement_note_id := replacement_row.id;
|
|
350
|
+
root_session_id := replacement_row.root_session_id;
|
|
351
|
+
replacement_kind := replacement_row.kind;
|
|
352
|
+
replacement_text := replacement_row.text;
|
|
353
|
+
replacement_status := replacement_row.status;
|
|
354
|
+
replacement_version := replacement_row.version;
|
|
355
|
+
replacement_expires_at := replacement_row.expires_at;
|
|
356
|
+
replacement_created_at := replacement_row.created_at;
|
|
357
|
+
replacement_updated_at := replacement_row.updated_at;
|
|
358
|
+
replacement_archived_at := replacement_row.archived_at;
|
|
359
|
+
replacement_actor_kind := replacement_row.created_by_actor_kind;
|
|
360
|
+
replacement_source_session_id := replacement_row.created_by_session_id;
|
|
361
|
+
replacement_source_turn_id := replacement_row.created_by_turn_id;
|
|
362
|
+
RETURN NEXT;
|
|
363
|
+
END;
|
|
364
|
+
$$;
|
|
365
|
+
|
|
366
|
+
RESET statement_timeout;
|
|
367
|
+
RESET lock_timeout;
|
|
368
|
+
|
|
369
|
+
DO $task_note_expiry_widen_hardening$
|
|
370
|
+
DECLARE
|
|
371
|
+
target_schema text := current_schema();
|
|
372
|
+
BEGIN
|
|
373
|
+
-- CREATE OR REPLACE FUNCTION drops the previously pinned search_path
|
|
374
|
+
-- (the FROM CURRENT clause re-resolves to the migration runner's
|
|
375
|
+
-- session search_path at DDL time, not the hardened value). Both functions
|
|
376
|
+
-- were originally re-pinned via ALTER FUNCTION in 0239/0260; re-apply the
|
|
377
|
+
-- same pin here.
|
|
378
|
+
EXECUTE format(
|
|
379
|
+
'ALTER FUNCTION %I.create_task_note_for_attempt('
|
|
380
|
+
|| 'uuid,uuid,uuid,uuid,uuid,integer,uuid,text,text,integer) '
|
|
381
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
382
|
+
target_schema, target_schema
|
|
383
|
+
);
|
|
384
|
+
EXECUTE format(
|
|
385
|
+
'ALTER FUNCTION %I.replace_task_note_for_attempt('
|
|
386
|
+
|| 'uuid,uuid,uuid,uuid,uuid,integer,uuid,uuid,uuid,uuid,integer,text,text,integer,text) '
|
|
387
|
+
|| 'SET search_path = pg_catalog, %I, pg_temp',
|
|
388
|
+
target_schema, target_schema
|
|
389
|
+
);
|
|
390
|
+
END
|
|
391
|
+
$task_note_expiry_widen_hardening$;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Persist the requires_action open suffix on pending tool-call receipts so
|
|
3
|
+
-- resume can pair history without materializing an SDK generatedItems heap.
|
|
4
|
+
ALTER TABLE "session_pending_tool_calls"
|
|
5
|
+
ADD COLUMN "interruption_kind" text,
|
|
6
|
+
ADD COLUMN "tied_reasoning_items" jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
7
|
+
ADD COLUMN "tied_reasoning_items_codec_version" integer;
|
|
8
|
+
|
|
9
|
+
ALTER TABLE "session_pending_tool_calls"
|
|
10
|
+
ADD CONSTRAINT "session_pending_tool_calls_interruption_kind_chk" CHECK (
|
|
11
|
+
"interruption_kind" IS NULL
|
|
12
|
+
OR "interruption_kind" IN ('human_input', 'approval', 'interaction_intervention')
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
ALTER TABLE "session_pending_tool_calls"
|
|
16
|
+
ADD CONSTRAINT "session_pending_tool_calls_tied_reasoning_items_chk" CHECK (
|
|
17
|
+
jsonb_typeof("tied_reasoning_items") = 'array'
|
|
18
|
+
);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- A Chrome profile keeps one stable device id across OpenGeni Agent reinstalls.
|
|
3
|
+
-- Re-enrollment creates a new enrollment row, so let the live enrollment reclaim
|
|
4
|
+
-- that endpoint only after its previous enrollment has been revoked. Active-to-
|
|
5
|
+
-- active reassignment remains forbidden.
|
|
6
|
+
|
|
7
|
+
SET LOCAL lock_timeout = '5s';
|
|
8
|
+
SET LOCAL statement_timeout = '30s';
|
|
9
|
+
|
|
10
|
+
CREATE OR REPLACE FUNCTION opengeni_private.attached_browser_devices_update_guard()
|
|
11
|
+
RETURNS trigger
|
|
12
|
+
LANGUAGE plpgsql
|
|
13
|
+
SET search_path FROM CURRENT
|
|
14
|
+
AS $guard$
|
|
15
|
+
DECLARE
|
|
16
|
+
enrollment_transfer_allowed boolean;
|
|
17
|
+
BEGIN
|
|
18
|
+
IF ROW(
|
|
19
|
+
NEW.id, NEW.account_id, NEW.workspace_id, NEW.created_at
|
|
20
|
+
) IS DISTINCT FROM ROW(
|
|
21
|
+
OLD.id, OLD.account_id, OLD.workspace_id, OLD.created_at
|
|
22
|
+
) THEN
|
|
23
|
+
RAISE EXCEPTION 'Attached browser endpoint identity cannot change'
|
|
24
|
+
USING ERRCODE = '23514';
|
|
25
|
+
END IF;
|
|
26
|
+
|
|
27
|
+
IF NEW.enrollment_id IS DISTINCT FROM OLD.enrollment_id THEN
|
|
28
|
+
SELECT
|
|
29
|
+
EXISTS (
|
|
30
|
+
SELECT 1
|
|
31
|
+
FROM enrollments previous_enrollment
|
|
32
|
+
WHERE previous_enrollment.id = OLD.enrollment_id
|
|
33
|
+
AND previous_enrollment.account_id = OLD.account_id
|
|
34
|
+
AND previous_enrollment.workspace_id = OLD.workspace_id
|
|
35
|
+
AND previous_enrollment.status = 'revoked'
|
|
36
|
+
)
|
|
37
|
+
AND EXISTS (
|
|
38
|
+
SELECT 1
|
|
39
|
+
FROM enrollments next_enrollment
|
|
40
|
+
WHERE next_enrollment.id = NEW.enrollment_id
|
|
41
|
+
AND next_enrollment.account_id = NEW.account_id
|
|
42
|
+
AND next_enrollment.workspace_id = NEW.workspace_id
|
|
43
|
+
AND next_enrollment.status = 'active'
|
|
44
|
+
)
|
|
45
|
+
INTO enrollment_transfer_allowed;
|
|
46
|
+
|
|
47
|
+
IF NOT coalesce(enrollment_transfer_allowed, false) THEN
|
|
48
|
+
RAISE EXCEPTION 'Attached browser endpoint enrollment can move only from revoked to active'
|
|
49
|
+
USING ERRCODE = '23514';
|
|
50
|
+
END IF;
|
|
51
|
+
END IF;
|
|
52
|
+
|
|
53
|
+
IF NEW.last_seen_at < OLD.last_seen_at OR NEW.updated_at < OLD.updated_at THEN
|
|
54
|
+
RAISE EXCEPTION 'Attached browser endpoint time cannot move backwards'
|
|
55
|
+
USING ERRCODE = '23514';
|
|
56
|
+
END IF;
|
|
57
|
+
IF NEW.connection_generation = OLD.connection_generation
|
|
58
|
+
AND NEW.inventory_revision < OLD.inventory_revision THEN
|
|
59
|
+
RAISE EXCEPTION 'Attached browser inventory revision cannot move backwards'
|
|
60
|
+
USING ERRCODE = '23514';
|
|
61
|
+
END IF;
|
|
62
|
+
RETURN NEW;
|
|
63
|
+
END
|
|
64
|
+
$guard$;
|