@opengeni/db 0.36.1 → 3.1.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-GYO7ED2Q.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-VTPRIXKG.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-HSOWSYOX.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-5KTIEDX7.js → chunk-PR2WMLOQ.js} +7714 -4443
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-Z7VYMNPZ.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.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-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.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 +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.d.ts +10 -0
- package/dist/editable-artifact-durable-export.js +18 -3
- package/dist/editable-artifact-durable-export.js.map +1 -1
- 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 +1633 -87
- package/dist/index.js +22004 -8109
- 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/knowledge-source-sync-schema.d.ts +730 -0
- package/dist/knowledge-source-sync.d.ts +60 -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 +23 -9
- package/dist/schema.d.ts +24925 -13697
- package/dist/schema.js +103 -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 +42 -5
- package/dist/session-realtime-ledger.d.ts +3 -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 +153 -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 +1 -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/0240_model_context_user_messages.sql +204 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0243_google_drive_object_acl_authority.sql +554 -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/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 +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifact-durable-export.ts +27 -0
- 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 +12001 -1699
- 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/knowledge-source-sync-schema.ts +83 -0
- package/src/knowledge-source-sync.ts +530 -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 +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +931 -24
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1315 -52
- package/src/schema.ts +3336 -438
- 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 +450 -98
- package/src/session-realtime-context.ts +19 -3
- package/src/session-realtime-ledger.ts +75 -12
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -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 +17 -1
- 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-5KTIEDX7.js.map +0 -1
- package/dist/chunk-FPVAD4CO.js +0 -117
- package/dist/chunk-FPVAD4CO.js.map +0 -1
- package/dist/chunk-HSOWSYOX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-VKLB2ZGI.js +0 -2389
- package/dist/chunk-VKLB2ZGI.js.map +0 -1
- package/dist/chunk-Z7VYMNPZ.js.map +0 -1
- /package/dist/{chunk-GYO7ED2Q.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-VTPRIXKG.js.map → chunk-NJVZPWXY.js.map} +0 -0
package/src/session-tenancy.ts
CHANGED
|
@@ -1,15 +1,93 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { sql } from "drizzle-orm";
|
|
3
|
+
import {
|
|
4
|
+
SessionTenancyBlocker as SessionTenancyBlockerSchema,
|
|
5
|
+
type SessionTenancyBlocker,
|
|
6
|
+
} from "@opengeni/contracts";
|
|
3
7
|
import {
|
|
4
8
|
rawRows,
|
|
5
9
|
type Database,
|
|
6
10
|
rlsContextForWorkspace,
|
|
11
|
+
withWorkspaceRls,
|
|
7
12
|
withWorkspaceSubjectRls,
|
|
8
13
|
withWorkspaceSubjectSessionActivityRls,
|
|
9
14
|
} from "./database";
|
|
15
|
+
import { nestedPostgresSqlState } from "./persistence-errors";
|
|
16
|
+
|
|
17
|
+
const SESSION_TENANCY_ACTIVATION_VERSION = 1;
|
|
18
|
+
|
|
19
|
+
export class SessionTenancyConflictError extends Error {
|
|
20
|
+
readonly name = "SessionTenancyConflictError";
|
|
21
|
+
constructor(
|
|
22
|
+
readonly reason: "not_quiescent" | "authority_epoch" | "operation_reuse",
|
|
23
|
+
readonly blocker: SessionTenancyBlocker | null = null,
|
|
24
|
+
options?: ErrorOptions,
|
|
25
|
+
) {
|
|
26
|
+
super(
|
|
27
|
+
reason === "not_quiescent"
|
|
28
|
+
? "Session must be fully quiescent before its tenancy can change"
|
|
29
|
+
: reason === "authority_epoch"
|
|
30
|
+
? "Session authority changed before the operation committed"
|
|
31
|
+
: "Session tenancy operation key was reused with different input",
|
|
32
|
+
options,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class SessionTenancyNotActivatedError extends Error {
|
|
38
|
+
readonly name = "SessionTenancyNotActivatedError";
|
|
39
|
+
constructor(options?: ErrorOptions) {
|
|
40
|
+
super("Session tenancy product surface is not activated for this organization", options);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class SessionTenancyAccessError extends Error {
|
|
45
|
+
readonly name = "SessionTenancyAccessError";
|
|
46
|
+
constructor(options?: ErrorOptions) {
|
|
47
|
+
super("Session tenancy target is unavailable", options);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class SessionTenancyInvalidRequestError extends Error {
|
|
52
|
+
readonly name = "SessionTenancyInvalidRequestError";
|
|
53
|
+
constructor(options?: ErrorOptions) {
|
|
54
|
+
super("Session tenancy request is invalid", options);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return Boolean(value && typeof value === "object");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Extract only the fixed quiescence DETAIL vocabulary, never arbitrary driver detail. */
|
|
63
|
+
export function nestedSessionTenancyBlocker(error: unknown): SessionTenancyBlocker | null {
|
|
64
|
+
const queue: unknown[] = [error];
|
|
65
|
+
const seen = new Set<unknown>();
|
|
66
|
+
while (queue.length > 0 && seen.size < 64) {
|
|
67
|
+
const current = queue.shift();
|
|
68
|
+
if (!isRecord(current) || seen.has(current)) continue;
|
|
69
|
+
seen.add(current);
|
|
70
|
+
for (const key of ["detail", "detailMessage"] as const) {
|
|
71
|
+
const parsed = SessionTenancyBlockerSchema.safeParse(current[key]);
|
|
72
|
+
if (parsed.success) return parsed.data;
|
|
73
|
+
}
|
|
74
|
+
for (const key of ["cause", "original", "driverError", "error", "errors"] as const) {
|
|
75
|
+
const nested = current[key];
|
|
76
|
+
if (Array.isArray(nested)) queue.push(...nested);
|
|
77
|
+
else if (nested !== undefined) queue.push(nested);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
10
82
|
|
|
11
83
|
export type SessionTenancyVisibility = "user_private" | "workspace_shared";
|
|
12
84
|
|
|
85
|
+
export type PrivateSessionCreatePolicy = {
|
|
86
|
+
personalWorkspace: boolean;
|
|
87
|
+
platformAvailable: boolean;
|
|
88
|
+
organizationEnabled: boolean;
|
|
89
|
+
};
|
|
90
|
+
|
|
13
91
|
export type TransitionSessionVisibilityInput = {
|
|
14
92
|
workspaceId: string;
|
|
15
93
|
sessionId: string;
|
|
@@ -21,6 +99,8 @@ export type TransitionSessionVisibilityInput = {
|
|
|
21
99
|
|
|
22
100
|
export type TransitionSessionVisibilityResult = {
|
|
23
101
|
operationId: string;
|
|
102
|
+
eventId: string | null;
|
|
103
|
+
eventSequence: number | null;
|
|
24
104
|
visibility: SessionTenancyVisibility;
|
|
25
105
|
authorityEpoch: number;
|
|
26
106
|
ownerOrganizationMembershipId: string | null;
|
|
@@ -39,11 +119,14 @@ export type ForkSessionContentInput = {
|
|
|
39
119
|
actorSubjectId: string;
|
|
40
120
|
destinationWorkspaceId: string;
|
|
41
121
|
destinationVisibility: SessionTenancyVisibility;
|
|
122
|
+
workspaceSharedAcknowledged: boolean;
|
|
42
123
|
operationKey: string;
|
|
43
124
|
};
|
|
44
125
|
|
|
45
126
|
export type ForkSessionContentResult = {
|
|
46
127
|
operationId: string;
|
|
128
|
+
eventId: string;
|
|
129
|
+
eventSequence: number;
|
|
47
130
|
sessionId: string;
|
|
48
131
|
workspaceId: string;
|
|
49
132
|
visibility: SessionTenancyVisibility;
|
|
@@ -52,6 +135,175 @@ export type ForkSessionContentResult = {
|
|
|
52
135
|
replay: boolean;
|
|
53
136
|
};
|
|
54
137
|
|
|
138
|
+
export async function sessionTenancyProductActivated(
|
|
139
|
+
db: Database,
|
|
140
|
+
workspaceId: string,
|
|
141
|
+
): Promise<boolean> {
|
|
142
|
+
const { accountId } = await rlsContextForWorkspace(db, workspaceId);
|
|
143
|
+
return await withWorkspaceRls(db, workspaceId, async (scopedDb) => {
|
|
144
|
+
const rows = await rawRows<{ activated: boolean }>(
|
|
145
|
+
scopedDb,
|
|
146
|
+
sql`select session_tenancy_product_activated(
|
|
147
|
+
${accountId}::uuid,
|
|
148
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
149
|
+
) as activated`,
|
|
150
|
+
);
|
|
151
|
+
return rows[0]?.activated === true;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function getPrivateSessionCreatePolicy(
|
|
156
|
+
db: Database,
|
|
157
|
+
input: { workspaceId: string; actorSubjectId: string },
|
|
158
|
+
): Promise<PrivateSessionCreatePolicy> {
|
|
159
|
+
const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);
|
|
160
|
+
return await withWorkspaceSubjectRls(
|
|
161
|
+
db,
|
|
162
|
+
input.workspaceId,
|
|
163
|
+
input.actorSubjectId,
|
|
164
|
+
async (scopedDb) => {
|
|
165
|
+
const rows = await rawRows<{
|
|
166
|
+
personalWorkspace: boolean;
|
|
167
|
+
platformAvailable: boolean;
|
|
168
|
+
organizationEnabled: boolean;
|
|
169
|
+
}>(
|
|
170
|
+
scopedDb,
|
|
171
|
+
sql`select
|
|
172
|
+
personal_workspace as "personalWorkspace",
|
|
173
|
+
platform_available as "platformAvailable",
|
|
174
|
+
organization_enabled as "organizationEnabled"
|
|
175
|
+
from get_private_session_create_policy(
|
|
176
|
+
${accountId}::uuid,
|
|
177
|
+
${input.workspaceId}::uuid,
|
|
178
|
+
${input.actorSubjectId}
|
|
179
|
+
)`,
|
|
180
|
+
);
|
|
181
|
+
const policy = rows[0];
|
|
182
|
+
if (!policy) throw new SessionTenancyAccessError();
|
|
183
|
+
return policy;
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Open the exact transaction-local trigger capability used by atomic private create. */
|
|
189
|
+
export async function openPrivateSessionCreateCapability(
|
|
190
|
+
db: Database,
|
|
191
|
+
input: { accountId: string; workspaceId: string; sessionId: string; actorSubjectId: string },
|
|
192
|
+
): Promise<{ capabilityId: string; ownerMembershipId: string }> {
|
|
193
|
+
let rows: Array<{ capabilityId: string; ownerMembershipId: string }>;
|
|
194
|
+
try {
|
|
195
|
+
rows = await rawRows<{ capabilityId: string; ownerMembershipId: string }>(
|
|
196
|
+
db,
|
|
197
|
+
sql`select
|
|
198
|
+
capability_id as "capabilityId",
|
|
199
|
+
owner_membership_id as "ownerMembershipId"
|
|
200
|
+
from open_private_session_create_capability(
|
|
201
|
+
${input.accountId}::uuid,
|
|
202
|
+
${input.workspaceId}::uuid,
|
|
203
|
+
${input.sessionId}::uuid,
|
|
204
|
+
${input.actorSubjectId}
|
|
205
|
+
)`,
|
|
206
|
+
);
|
|
207
|
+
} catch (error) {
|
|
208
|
+
// The definer raises 55000 only when the organization owner/admin setting
|
|
209
|
+
// does not permit a new private session in this shared workspace; every
|
|
210
|
+
// authority failure remains 42501 and propagates unchanged.
|
|
211
|
+
if (nestedPostgresSqlState(error) === "55000") {
|
|
212
|
+
throw new SessionTenancyNotActivatedError({ cause: error });
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
const capabilityId = rows[0]?.capabilityId;
|
|
217
|
+
const ownerMembershipId = rows[0]?.ownerMembershipId;
|
|
218
|
+
if (!capabilityId || !ownerMembershipId) {
|
|
219
|
+
throw new Error("Private session create capability was not returned");
|
|
220
|
+
}
|
|
221
|
+
return { capabilityId, ownerMembershipId };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Open the exact transaction-local capability for one private child insert. */
|
|
225
|
+
export async function openPrivateChildSessionCreateCapability(
|
|
226
|
+
db: Database,
|
|
227
|
+
input: {
|
|
228
|
+
accountId: string;
|
|
229
|
+
workspaceId: string;
|
|
230
|
+
sessionId: string;
|
|
231
|
+
parentSessionId: string;
|
|
232
|
+
actorTurnId: string;
|
|
233
|
+
actorAttemptId: string;
|
|
234
|
+
actorExecutionGeneration: number;
|
|
235
|
+
},
|
|
236
|
+
): Promise<{ capabilityId: string; ownerMembershipId: string; ownerSubjectId: string }> {
|
|
237
|
+
const rows = await rawRows<{
|
|
238
|
+
capabilityId: string;
|
|
239
|
+
ownerMembershipId: string;
|
|
240
|
+
ownerSubjectId: string;
|
|
241
|
+
}>(
|
|
242
|
+
db,
|
|
243
|
+
sql`select
|
|
244
|
+
capability_id as "capabilityId",
|
|
245
|
+
owner_membership_id as "ownerMembershipId",
|
|
246
|
+
owner_subject_id as "ownerSubjectId"
|
|
247
|
+
from open_private_child_session_create_capability(
|
|
248
|
+
${input.accountId}::uuid,
|
|
249
|
+
${input.workspaceId}::uuid,
|
|
250
|
+
${input.sessionId}::uuid,
|
|
251
|
+
${input.parentSessionId}::uuid,
|
|
252
|
+
${input.actorTurnId}::uuid,
|
|
253
|
+
${input.actorAttemptId}::uuid,
|
|
254
|
+
${input.actorExecutionGeneration}::integer
|
|
255
|
+
)`,
|
|
256
|
+
);
|
|
257
|
+
const capabilityId = rows[0]?.capabilityId;
|
|
258
|
+
const ownerMembershipId = rows[0]?.ownerMembershipId;
|
|
259
|
+
const ownerSubjectId = rows[0]?.ownerSubjectId;
|
|
260
|
+
if (!capabilityId || !ownerMembershipId || !ownerSubjectId) {
|
|
261
|
+
throw new Error("Private child session create capability was not returned");
|
|
262
|
+
}
|
|
263
|
+
return { capabilityId, ownerMembershipId, ownerSubjectId };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function closePrivateSessionCreateCapability(
|
|
267
|
+
db: Database,
|
|
268
|
+
capabilityId: string,
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
await db.execute(sql`select close_private_session_create_capability(${capabilityId}::uuid)`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function assertSessionTenancyProductActivated(
|
|
274
|
+
db: Database,
|
|
275
|
+
workspaceId: string,
|
|
276
|
+
): Promise<void> {
|
|
277
|
+
if (!(await sessionTenancyProductActivated(db, workspaceId))) {
|
|
278
|
+
throw new SessionTenancyNotActivatedError();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function mapSessionTenancyPersistenceError(
|
|
283
|
+
error: unknown,
|
|
284
|
+
options: { authorityEpochConflict: boolean },
|
|
285
|
+
): never {
|
|
286
|
+
const state = nestedPostgresSqlState(error);
|
|
287
|
+
if (state === "55P03") {
|
|
288
|
+
throw new SessionTenancyConflictError("not_quiescent", nestedSessionTenancyBlocker(error), {
|
|
289
|
+
cause: error,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
if (state === "40001" && options.authorityEpochConflict) {
|
|
293
|
+
throw new SessionTenancyConflictError("authority_epoch", null, { cause: error });
|
|
294
|
+
}
|
|
295
|
+
if (state === "23505") {
|
|
296
|
+
throw new SessionTenancyConflictError("operation_reuse", null, { cause: error });
|
|
297
|
+
}
|
|
298
|
+
if (state === "42501" || state === "P0002") {
|
|
299
|
+
throw new SessionTenancyAccessError({ cause: error });
|
|
300
|
+
}
|
|
301
|
+
if (state === "22023") {
|
|
302
|
+
throw new SessionTenancyInvalidRequestError({ cause: error });
|
|
303
|
+
}
|
|
304
|
+
throw error;
|
|
305
|
+
}
|
|
306
|
+
|
|
55
307
|
export function canonicalSessionVisibilityTransitionHash(
|
|
56
308
|
input: Pick<
|
|
57
309
|
TransitionSessionVisibilityInput,
|
|
@@ -73,17 +325,32 @@ export function canonicalSessionVisibilityTransitionHash(
|
|
|
73
325
|
export function canonicalSessionForkHash(
|
|
74
326
|
input: Pick<
|
|
75
327
|
ForkSessionContentInput,
|
|
76
|
-
|
|
328
|
+
| "sourceSessionId"
|
|
329
|
+
| "destinationWorkspaceId"
|
|
330
|
+
| "destinationVisibility"
|
|
331
|
+
| "workspaceSharedAcknowledged"
|
|
77
332
|
>,
|
|
78
333
|
): string {
|
|
334
|
+
const legacyCompatiblePrivateRequest =
|
|
335
|
+
input.destinationVisibility === "user_private" && !input.workspaceSharedAcknowledged;
|
|
79
336
|
return createHash("sha256")
|
|
80
337
|
.update(
|
|
81
|
-
JSON.stringify(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
338
|
+
JSON.stringify(
|
|
339
|
+
legacyCompatiblePrivateRequest
|
|
340
|
+
? {
|
|
341
|
+
version: 1,
|
|
342
|
+
sourceSessionId: input.sourceSessionId,
|
|
343
|
+
destinationWorkspaceId: input.destinationWorkspaceId,
|
|
344
|
+
destinationVisibility: input.destinationVisibility,
|
|
345
|
+
}
|
|
346
|
+
: {
|
|
347
|
+
version: 2,
|
|
348
|
+
sourceSessionId: input.sourceSessionId,
|
|
349
|
+
destinationWorkspaceId: input.destinationWorkspaceId,
|
|
350
|
+
destinationVisibility: input.destinationVisibility,
|
|
351
|
+
workspaceSharedAcknowledged: input.workspaceSharedAcknowledged,
|
|
352
|
+
},
|
|
353
|
+
),
|
|
87
354
|
)
|
|
88
355
|
.digest("hex");
|
|
89
356
|
}
|
|
@@ -98,27 +365,33 @@ export async function transitionSessionVisibility(
|
|
|
98
365
|
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
99
366
|
const requestHash = canonicalSessionVisibilityTransitionHash(input);
|
|
100
367
|
const { accountId } = await rlsContextForWorkspace(db, input.workspaceId);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
368
|
+
await assertSessionTenancyProductActivated(db, input.workspaceId);
|
|
369
|
+
try {
|
|
370
|
+
return await withWorkspaceSubjectSessionActivityRls(
|
|
371
|
+
db,
|
|
372
|
+
input.workspaceId,
|
|
373
|
+
input.actorSubjectId,
|
|
374
|
+
async (scopedDb) => {
|
|
375
|
+
const rows = await rawRows<{
|
|
376
|
+
operationId: string;
|
|
377
|
+
eventId: string | null;
|
|
378
|
+
eventSequence: number | null;
|
|
379
|
+
visibility: SessionTenancyVisibility;
|
|
380
|
+
authorityEpoch: number;
|
|
381
|
+
ownerOrganizationMembershipId: string | null;
|
|
382
|
+
changed: boolean;
|
|
383
|
+
replay: boolean;
|
|
384
|
+
interruptedAttemptCount: number;
|
|
385
|
+
cancelledTurnCount: number;
|
|
386
|
+
cancelledUpdateCount: number;
|
|
387
|
+
pausedGoalCount: number;
|
|
388
|
+
revokedGrantCount: number;
|
|
389
|
+
}>(
|
|
390
|
+
scopedDb,
|
|
391
|
+
sql`select
|
|
121
392
|
operation_id as "operationId",
|
|
393
|
+
event_id as "eventId",
|
|
394
|
+
event_sequence as "eventSequence",
|
|
122
395
|
visibility,
|
|
123
396
|
authority_epoch as "authorityEpoch",
|
|
124
397
|
owner_organization_membership_id as "ownerOrganizationMembershipId",
|
|
@@ -137,14 +410,18 @@ export async function transitionSessionVisibility(
|
|
|
137
410
|
${input.targetVisibility},
|
|
138
411
|
${input.expectedAuthorityEpoch},
|
|
139
412
|
${input.operationKey},
|
|
140
|
-
${requestHash}
|
|
413
|
+
${requestHash},
|
|
414
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
141
415
|
)`,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
416
|
+
);
|
|
417
|
+
const result = rows[0];
|
|
418
|
+
if (!result) throw new Error("Session visibility transition returned no result");
|
|
419
|
+
return result;
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
} catch (error) {
|
|
423
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: true });
|
|
424
|
+
}
|
|
148
425
|
}
|
|
149
426
|
|
|
150
427
|
export async function forkSessionContent(
|
|
@@ -152,17 +429,24 @@ export async function forkSessionContent(
|
|
|
152
429
|
input: ForkSessionContentInput,
|
|
153
430
|
): Promise<ForkSessionContentResult> {
|
|
154
431
|
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
432
|
+
if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {
|
|
433
|
+
throw new Error("The first session fork contract is same-workspace only");
|
|
434
|
+
}
|
|
155
435
|
const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);
|
|
436
|
+
await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);
|
|
156
437
|
const requestHash = canonicalSessionForkHash(input);
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
438
|
+
try {
|
|
439
|
+
return await withWorkspaceSubjectRls(
|
|
440
|
+
db,
|
|
441
|
+
input.sourceWorkspaceId,
|
|
442
|
+
input.actorSubjectId,
|
|
443
|
+
async (scopedDb) => {
|
|
444
|
+
const rows = await rawRows<ForkSessionContentResult>(
|
|
445
|
+
scopedDb,
|
|
446
|
+
sql`select
|
|
165
447
|
operation_id as "operationId",
|
|
448
|
+
event_id as "eventId",
|
|
449
|
+
event_sequence as "eventSequence",
|
|
166
450
|
session_id as "sessionId",
|
|
167
451
|
workspace_id as "workspaceId",
|
|
168
452
|
visibility,
|
|
@@ -176,13 +460,83 @@ export async function forkSessionContent(
|
|
|
176
460
|
${input.actorSubjectId},
|
|
177
461
|
${input.destinationWorkspaceId}::uuid,
|
|
178
462
|
${input.destinationVisibility},
|
|
463
|
+
${input.workspaceSharedAcknowledged},
|
|
179
464
|
${input.operationKey},
|
|
180
|
-
${requestHash}
|
|
465
|
+
${requestHash},
|
|
466
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
181
467
|
)`,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
468
|
+
);
|
|
469
|
+
const result = rows[0];
|
|
470
|
+
if (!result) throw new Error("Session fork returned no result");
|
|
471
|
+
return result;
|
|
472
|
+
},
|
|
473
|
+
);
|
|
474
|
+
} catch (error) {
|
|
475
|
+
// A private destination inside a shared workspace carries the same
|
|
476
|
+
// organization owner/admin product decision the create path enforces
|
|
477
|
+
// (migration 0323), and the fork routine raises the same 55000. Surface it
|
|
478
|
+
// as the not-activated product state rather than an opaque database error.
|
|
479
|
+
if (nestedPostgresSqlState(error) === "55000") {
|
|
480
|
+
throw new SessionTenancyNotActivatedError({ cause: error });
|
|
481
|
+
}
|
|
482
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Recover one exact committed fork before mutable source-session authorization.
|
|
488
|
+
*
|
|
489
|
+
* The database capability requires the authenticated actor's active workspace
|
|
490
|
+
* authority and matches the complete actor/workspace/source/key/request-hash
|
|
491
|
+
* tuple. A fresh key returns null; changed intent raises the ordinary tenancy
|
|
492
|
+
* idempotency conflict. This is receipt recovery, not session discovery.
|
|
493
|
+
*/
|
|
494
|
+
export async function replayAppliedSessionFork(
|
|
495
|
+
db: Database,
|
|
496
|
+
input: ForkSessionContentInput,
|
|
497
|
+
): Promise<ForkSessionContentResult | null> {
|
|
498
|
+
if (!input.operationKey.trim()) throw new Error("operationKey must not be empty");
|
|
499
|
+
if (input.destinationWorkspaceId !== input.sourceWorkspaceId) {
|
|
500
|
+
throw new Error("The first session fork contract is same-workspace only");
|
|
501
|
+
}
|
|
502
|
+
const { accountId } = await rlsContextForWorkspace(db, input.sourceWorkspaceId);
|
|
503
|
+
await assertSessionTenancyProductActivated(db, input.sourceWorkspaceId);
|
|
504
|
+
const requestHash = canonicalSessionForkHash(input);
|
|
505
|
+
try {
|
|
506
|
+
return await withWorkspaceSubjectRls(
|
|
507
|
+
db,
|
|
508
|
+
input.sourceWorkspaceId,
|
|
509
|
+
input.actorSubjectId,
|
|
510
|
+
async (scopedDb) => {
|
|
511
|
+
const rows = await rawRows<ForkSessionContentResult>(
|
|
512
|
+
scopedDb,
|
|
513
|
+
sql`select
|
|
514
|
+
operation_id as "operationId",
|
|
515
|
+
event_id as "eventId",
|
|
516
|
+
event_sequence as "eventSequence",
|
|
517
|
+
session_id as "sessionId",
|
|
518
|
+
workspace_id as "workspaceId",
|
|
519
|
+
visibility,
|
|
520
|
+
authority_epoch as "authorityEpoch",
|
|
521
|
+
copied_history_item_count as "copiedHistoryItemCount",
|
|
522
|
+
replay
|
|
523
|
+
from replay_applied_session_fork(
|
|
524
|
+
${accountId}::uuid,
|
|
525
|
+
${input.sourceWorkspaceId}::uuid,
|
|
526
|
+
${input.sourceSessionId}::uuid,
|
|
527
|
+
${input.actorSubjectId},
|
|
528
|
+
${input.destinationWorkspaceId}::uuid,
|
|
529
|
+
${input.destinationVisibility},
|
|
530
|
+
${input.workspaceSharedAcknowledged},
|
|
531
|
+
${input.operationKey},
|
|
532
|
+
${requestHash},
|
|
533
|
+
${SESSION_TENANCY_ACTIVATION_VERSION}
|
|
534
|
+
)`,
|
|
535
|
+
);
|
|
536
|
+
return rows[0] ?? null;
|
|
537
|
+
},
|
|
538
|
+
);
|
|
539
|
+
} catch (error) {
|
|
540
|
+
mapSessionTenancyPersistenceError(error, { authorityEpochConflict: false });
|
|
541
|
+
}
|
|
188
542
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SessionEvent } from "@opengeni/contracts";
|
|
2
|
-
import {
|
|
3
|
-
import { and, asc, eq, sql } from "drizzle-orm";
|
|
2
|
+
import { canonicalizePersistedHistoryItem, omitOutputOnlyHistoryItemFields } from "@opengeni/codex";
|
|
3
|
+
import { and, asc, eq, inArray, sql } from "drizzle-orm";
|
|
4
4
|
import type { Database } from "./database";
|
|
5
5
|
import { fromPostgresLosslessJson, LOSSLESS_CONTENT_CODEC_VERSION } from "./lossless-json";
|
|
6
6
|
import * as schema from "./schema";
|
|
@@ -85,6 +85,8 @@ export type ClosePendingSessionToolCallsInput = {
|
|
|
85
85
|
reason: string;
|
|
86
86
|
sequence: number;
|
|
87
87
|
now: Date;
|
|
88
|
+
turnAssociation?: "current" | null;
|
|
89
|
+
preserveInterruptionRows?: boolean;
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
function mapEvent(row: typeof schema.sessionEvents.$inferSelect): SessionEvent {
|
|
@@ -107,10 +109,13 @@ function mapEvent(row: typeof schema.sessionEvents.$inferSelect): SessionEvent {
|
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
/**
|
|
110
|
-
* Close
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
112
|
+
* Close raw tool calls for a logical turn while its owning session/turn locks
|
|
113
|
+
* are held. Recoverable transitions preserve interruption rows because those
|
|
114
|
+
* rows are the exact open-suffix resume authority for a replacement attempt;
|
|
115
|
+
* ordinary in-flight rows still receive durable outcome-unknown settlement.
|
|
116
|
+
* Terminal and superseding transitions close every row. Durable results win;
|
|
117
|
+
* repeated settlement is an exact no-op after the selected ledger rows are
|
|
118
|
+
* deleted.
|
|
114
119
|
*/
|
|
115
120
|
export async function closePendingSessionToolCallsInTransaction(
|
|
116
121
|
tx: Database,
|
|
@@ -128,18 +133,20 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
128
133
|
)
|
|
129
134
|
.orderBy(asc(schema.sessionPendingToolCalls.createdAt), asc(schema.sessionPendingToolCalls.id))
|
|
130
135
|
.for("update");
|
|
131
|
-
const pending = pendingRows
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
row.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
const pending = pendingRows
|
|
137
|
+
.filter((row) => !(input.preserveInterruptionRows && row.interruptionKind !== null))
|
|
138
|
+
.map((row) => ({
|
|
139
|
+
...row,
|
|
140
|
+
callItem: fromPostgresLosslessJson(row.callItem, row.callItemCodecVersion),
|
|
141
|
+
resultItem:
|
|
142
|
+
row.resultItem === null
|
|
143
|
+
? null
|
|
144
|
+
: fromPostgresLosslessJson(row.resultItem, row.resultItemCodecVersion),
|
|
145
|
+
eventOutput:
|
|
146
|
+
row.eventOutput === null
|
|
147
|
+
? null
|
|
148
|
+
: fromPostgresLosslessJson(row.eventOutput, row.eventOutputCodecVersion),
|
|
149
|
+
}));
|
|
143
150
|
if (pending.length === 0) return { sequence: input.sequence, events: [], closed: 0 };
|
|
144
151
|
|
|
145
152
|
const history = await tx
|
|
@@ -250,7 +257,7 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
250
257
|
sessionId: input.sessionId,
|
|
251
258
|
turnId: input.turnId,
|
|
252
259
|
position: nextPosition++,
|
|
253
|
-
item: resolution.call.callItem,
|
|
260
|
+
item: omitOutputOnlyHistoryItemFields(resolution.call.callItem),
|
|
254
261
|
itemCodecVersion: LOSSLESS_CONTENT_CODEC_VERSION,
|
|
255
262
|
active: true,
|
|
256
263
|
});
|
|
@@ -274,7 +281,7 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
274
281
|
sessionId: input.sessionId,
|
|
275
282
|
turnId: input.turnId,
|
|
276
283
|
position: nextPosition++,
|
|
277
|
-
item:
|
|
284
|
+
item: canonicalizePersistedHistoryItem(
|
|
278
285
|
resolution.result,
|
|
279
286
|
resolution.call.modelToolOutputTruncationTokens ?? undefined,
|
|
280
287
|
),
|
|
@@ -297,7 +304,7 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
297
304
|
turnId: input.turnId,
|
|
298
305
|
turnGeneration: resolution.call.executionGeneration,
|
|
299
306
|
turnAttemptId: resolution.call.attemptId,
|
|
300
|
-
turnAssociation: "current",
|
|
307
|
+
turnAssociation: input.turnAssociation === undefined ? "current" : input.turnAssociation,
|
|
301
308
|
payload: {
|
|
302
309
|
id: resolution.call.callId,
|
|
303
310
|
output: resolution.interrupted
|
|
@@ -341,14 +348,16 @@ export async function closePendingSessionToolCallsInTransaction(
|
|
|
341
348
|
reason: input.reason,
|
|
342
349
|
now: input.now,
|
|
343
350
|
});
|
|
344
|
-
await tx
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
+
await tx.delete(schema.sessionPendingToolCalls).where(
|
|
352
|
+
and(
|
|
353
|
+
eq(schema.sessionPendingToolCalls.workspaceId, input.workspaceId),
|
|
354
|
+
eq(schema.sessionPendingToolCalls.sessionId, input.sessionId),
|
|
355
|
+
eq(schema.sessionPendingToolCalls.turnId, input.turnId),
|
|
356
|
+
inArray(
|
|
357
|
+
schema.sessionPendingToolCalls.id,
|
|
358
|
+
pending.map((call) => call.id),
|
|
351
359
|
),
|
|
352
|
-
)
|
|
360
|
+
),
|
|
361
|
+
);
|
|
353
362
|
return { sequence, events: inserted.map(mapEvent), closed: pending.length };
|
|
354
363
|
}
|