@superatomai/sdk-node 0.0.87-dsp → 0.0.89-dsp

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/index.d.mts CHANGED
@@ -4480,6 +4480,56 @@ interface SourcePolicy {
4480
4480
  /** Grants outside the scope above. Empty or absent means none. */
4481
4481
  exceptions?: PolicyException[];
4482
4482
  }
4483
+ /**
4484
+ * Operators the constraint engine builds predicates from. A superset of
4485
+ * {@link RowFilter.op}, which the original rewriter is limited to.
4486
+ */
4487
+ type ConstraintOp = 'in' | 'not_in' | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'not_like' | 'is_null' | 'is_not_null';
4488
+ /**
4489
+ * One filter in a {@link ConstraintPolicy}.
4490
+ *
4491
+ * No `table`, unlike {@link RowFilter}: these are keyed by table, so a filter
4492
+ * cannot fail to match the table it was written for and leave it unfiltered.
4493
+ */
4494
+ interface ConstraintFilter {
4495
+ column: string;
4496
+ op?: ConstraintOp;
4497
+ /** Omitted only for `is_null` / `is_not_null`. */
4498
+ values?: unknown[];
4499
+ label?: string;
4500
+ }
4501
+ /**
4502
+ * What one user may see in one source, in the shape the constraint engine reads
4503
+ * (`sqlrewrite/v2/engine.py`).
4504
+ *
4505
+ * Not a superset of {@link SourcePolicy} but a different shape: it adds
4506
+ * schemas, columns and limits. A claim carries one shape or the other, never
4507
+ * both.
4508
+ */
4509
+ interface ConstraintPolicy {
4510
+ sourceId?: string | null;
4511
+ label?: string;
4512
+ /**
4513
+ * Every table is read from this schema, whatever schema the query wrote —
4514
+ * which is what makes it a restriction: there is no other schema to reach.
4515
+ * Absent means schemas are left exactly as written.
4516
+ */
4517
+ schema?: string;
4518
+ /** table → readable columns, or `null` for every column. Absent: every table. */
4519
+ readableTables?: Record<string, string[] | null>;
4520
+ /** table → filters, ANDed together. */
4521
+ rowFilters?: Record<string, ConstraintFilter[]>;
4522
+ limits?: {
4523
+ maxRows?: number;
4524
+ maxExecutionSeconds?: number;
4525
+ };
4526
+ /**
4527
+ * Why this policy could not be read. Set rather than dropping the policy:
4528
+ * a dropped policy leaves its source UNRESTRICTED, which is the opposite of
4529
+ * what a malformed restriction should mean. The gate refuses the source.
4530
+ */
4531
+ invalid?: string;
4532
+ }
4483
4533
  interface RequestContext {
4484
4534
  principal: Principal;
4485
4535
  projectId?: string;
@@ -4489,6 +4539,11 @@ interface RequestContext {
4489
4539
  * — see the backend's policy-gate.
4490
4540
  */
4491
4541
  policies?: SourcePolicy[];
4542
+ /**
4543
+ * The same restrictions in the constraint shape. Present only when the claim
4544
+ * said `version: 2`, and then `policies` is empty: one claim, one engine.
4545
+ */
4546
+ constraintPolicies?: ConstraintPolicy[];
4492
4547
  /**
4493
4548
  * The data sources this user may reach AT ALL, by source id.
4494
4549
  *
@@ -4541,6 +4596,7 @@ declare function runAsUser<T>(user: {
4541
4596
  projectId?: string;
4542
4597
  requestId?: string;
4543
4598
  policies?: SourcePolicy[];
4599
+ constraintPolicies?: ConstraintPolicy[];
4544
4600
  allowedSources?: string[] | null;
4545
4601
  graphVersion?: GraphVersion;
4546
4602
  }, fn: () => T): T;
@@ -4589,6 +4645,35 @@ declare function describeContext(ctx?: RequestContext | undefined): string;
4589
4645
  /** True when the context represents a signed-in user rather than a trusted job. */
4590
4646
  declare function isUserContext(ctx?: RequestContext | undefined): boolean;
4591
4647
 
4648
+ /**
4649
+ * The constraint policy shape (`version: 2`), read onto the request context.
4650
+ *
4651
+ * Strict, and failing CLOSED, which is the opposite of the legacy parser beside
4652
+ * it. There, dropping something malformed grants LESS. Here it grants MORE: a
4653
+ * dropped policy leaves its source with no restriction at all. So a policy that
4654
+ * cannot be read is kept, marked `invalid`, and the gate refuses the source.
4655
+ */
4656
+
4657
+ declare function constraintPoliciesFromConfig(raw: unknown): ConstraintPolicy[];
4658
+
4659
+ /**
4660
+ * The original policy shape, read out of the admin-authored per-user config.
4661
+ *
4662
+ * Every step here is defensive and DROPS what it cannot read, because the
4663
+ * config is free-form JSON typed by a human into an admin field. That is a
4664
+ * deliberate trade with a sharp edge — a typo'd policy silently becomes no
4665
+ * policy, i.e. unrestricted. The constraint shape next door takes the opposite
4666
+ * approach for exactly that reason.
4667
+ */
4668
+
4669
+ /**
4670
+ * Read access policies out of the admin-authored per-user config.
4671
+ *
4672
+ * Accepts either a single object or an array, so one user can be scoped across
4673
+ * several sources — and the object form, whose policies sit under `policies`.
4674
+ */
4675
+ declare function policiesFromConfig(config: unknown): SourcePolicy[];
4676
+
4592
4677
  /**
4593
4678
  * Deriving "who is asking" from an incoming WebSocket message.
4594
4679
  *
@@ -4603,6 +4688,8 @@ declare function isUserContext(ctx?: RequestContext | undefined): boolean;
4603
4688
  /** Everything one request's config says about what the caller may see. */
4604
4689
  interface ResolvedAccess {
4605
4690
  policies: SourcePolicy[];
4691
+ /** Set instead of `policies` when the claim said `version: 2`. */
4692
+ constraintPolicies: ConstraintPolicy[];
4606
4693
  /** `null` means every source; `[]` means none — see `RequestContext.allowedSources`. */
4607
4694
  allowedSources: string[] | null;
4608
4695
  }
@@ -4616,19 +4703,6 @@ interface ResolvedAccess {
4616
4703
  * drop EVERY row filter the customer wrote.
4617
4704
  */
4618
4705
  declare function accessFromConfig(config: unknown): ResolvedAccess;
4619
- /**
4620
- * Read access policies out of the admin-authored per-user config.
4621
- *
4622
- * The config is free-form JSON typed by a human into an admin field, so every
4623
- * step here is defensive: a malformed entry is DROPPED, never guessed at. That
4624
- * is a deliberate trade-off with a sharp edge — a typo'd policy silently
4625
- * becomes no policy, i.e. unrestricted. Validating shapes against the source
4626
- * schema (and refusing, rather than dropping) is the fix, and is not built yet.
4627
- *
4628
- * Accepts either a single object or an array, so one user can be scoped across
4629
- * several sources — and the object form, whose policies sit under `policies`.
4630
- */
4631
- declare function policiesFromConfig(config: unknown): SourcePolicy[];
4632
4706
  /**
4633
4707
  * Run `fn` with the caller's identity established.
4634
4708
  *
@@ -5238,7 +5312,8 @@ interface CodingAgentDefinition<TInput = unknown, TOutput = unknown> {
5238
5312
  promptDir?: string;
5239
5313
  /** Override the deployment default for this agent alone. */
5240
5314
  harness?: Harness;
5241
- model?: string;
5315
+ /** A function picks the model per run (e.g. by what the question carries); undefined = default. */
5316
+ model?: string | ((input: TInput, ctx: AgentRunContext) => string | undefined);
5242
5317
  /**
5243
5318
  * Let this agent's shell reach the network. Off by default.
5244
5319
  *
@@ -5454,13 +5529,6 @@ interface RunAgentOptions<TInput> extends AgentRunContext {
5454
5529
  */
5455
5530
  chat?: boolean;
5456
5531
  }
5457
- /**
5458
- * Run one agent, end to end.
5459
- *
5460
- * Never throws for an agent-side failure — a bad run comes back as a record
5461
- * with status 'error', because callers are usually crons and message handlers
5462
- * that must keep going. Genuinely programmer errors (unknown agent id) do throw.
5463
- */
5464
5532
  declare function runCodingAgent<TInput = unknown, TOutput = unknown>(agentId: string, options?: RunAgentOptions<TInput>): Promise<AgentRunRecord<TOutput>>;
5465
5533
 
5466
5534
  /**
@@ -6085,4 +6153,4 @@ declare class SuperatomSDK {
6085
6153
  getConversationSimilarityThreshold(): number;
6086
6154
  }
6087
6155
 
6088
- export { ALL_COLUMN_KEYS, ANSWER_COMPONENT_NAMES, type AgentConfig, type AgentEvent, type AgentResponse, type AgentRunContext, type AgentRunHandlers, type AgentRunRecord, type AgentRunResult, type AgentTrigger, type AgentUsage, type AnalyticsEventType, type AnalyticsStatus, type AnswerProvider, type AnswerProviderContext, type ApplySummary, BM25L, type BM25LOptions, type BaseLLMConfig, type BuildTriggerInput, CATEGORY_KEYS, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CodexSessionOptions, type CodingAgentCollectionConfig, type CodingAgentDefinition, type CodingAgentSession, type CodingAgentSessionOptions, type CodingAgentsConfig, type ColInfo, type ColType, type CollectionHandler, type CollectionOperation, type Component, type DBUIBlock, DEFAULT_AGENT_CONFIG, DEFAULT_FMT_CONFIG, type DashboardAgentCollectionConfig, type DatabaseType, type FindingCode, type Fmt, type FmtConfig, type GeneratedFile, type GraphVersion, type Harness, type HybridSearchOptions, INSTRUCTIONS_FILE, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type McpServerSpec, type Message, MissingRequestContextError, type ModelStrategy, NUMERIC_KEYS, type NarrationUpdate, type OutputContract, type OutputField, type OutputValidator, type PiSessionListener, type PiSessionState, type PiTranscriptEntry, type PolicyException, type PolicyExceptionTable, type Principal, type PromptAttachment, type PromptContext, type RequestContext, type RerankedResult, type ResolvedAccess, type RowFilter, type RunAgentOptions, STORAGE_CONFIG, type ScriptComponentSpec, ScriptMatcher, type ScriptParameter, type ScriptRecipe, type ScriptRecipeMetaRow, type ScriptRecipeStore, type ScriptResult, ScriptStore, type ScriptStoreOptions, type SelectedWorkflow, type ShimDef, type ShimSummary, type ShimTarget, type SourcePolicy, type SpecToValidate, SuperatomSDK, type SuperatomSDKConfig, type T_RESPONSE, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, type TriggerEvent, type TriggerFireInfo, UIBlock, UILogCollector, type User, UserManager, type UsersData, type ValidationContext, type ValidationFinding, type WorkflowDescriptor, type Workspace, accessFromConfig, analyticsClient, answerCatalogForBinding, answerComponents, anthropicLLM, assertReadOnlySql, bindContext, bridgePreamble, captureContext, clearContextTokens, contextTokenCount, createCodexSession, createCodingAgentSession, dashboardConversationHistory, dataFingerprint, describeContext, describeTriggers, emitAgentEvent, extractNamedExport, formatFindingsForAgent, geminiLLM, getCodingAgent, getCodingAgentsConfig, getPiSessionState, getRequestContext, groqLLM, hybridRerank, inferColumns, isUserContext, listCodingAgents, llmUsageLogger, logger, makeFmt, mintContextToken, normalizeScriptBody, onAgentRun, onPiSessionUpdate, openaiLLM, policiesFromConfig, projectForDatasetBinding, promptLoader, queryCache, registerCodingAgentCollection, registerCodingAgents, registerDashboardAgentCollection, requireRequestContext, rerankChromaResults, rerankConversationResults, resolveContextToken, resolveScriptRecipeStore, revokeContextToken, runAsSystem, runAsUser, runCodingAgent, runScript, runWithContext, runWithMessageIdentity, startAgentTriggers, stopAgentTriggers, userPromptErrorLogger, validateComponentSpec, withCapturedContext, writeBridgeShims, writeShims };
6156
+ export { ALL_COLUMN_KEYS, ANSWER_COMPONENT_NAMES, type AgentConfig, type AgentEvent, type AgentResponse, type AgentRunContext, type AgentRunHandlers, type AgentRunRecord, type AgentRunResult, type AgentTrigger, type AgentUsage, type AnalyticsEventType, type AnalyticsStatus, type AnswerProvider, type AnswerProviderContext, type ApplySummary, BM25L, type BM25LOptions, type BaseLLMConfig, type BuildTriggerInput, CATEGORY_KEYS, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CodexSessionOptions, type CodingAgentCollectionConfig, type CodingAgentDefinition, type CodingAgentSession, type CodingAgentSessionOptions, type CodingAgentsConfig, type ColInfo, type ColType, type CollectionHandler, type CollectionOperation, type Component, type ConstraintFilter, type ConstraintOp, type ConstraintPolicy, type DBUIBlock, DEFAULT_AGENT_CONFIG, DEFAULT_FMT_CONFIG, type DashboardAgentCollectionConfig, type DatabaseType, type FindingCode, type Fmt, type FmtConfig, type GeneratedFile, type GraphVersion, type Harness, type HybridSearchOptions, INSTRUCTIONS_FILE, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type McpServerSpec, type Message, MissingRequestContextError, type ModelStrategy, NUMERIC_KEYS, type NarrationUpdate, type OutputContract, type OutputField, type OutputValidator, type PiSessionListener, type PiSessionState, type PiTranscriptEntry, type PolicyException, type PolicyExceptionTable, type Principal, type PromptAttachment, type PromptContext, type RequestContext, type RerankedResult, type ResolvedAccess, type RowFilter, type RunAgentOptions, STORAGE_CONFIG, type ScriptComponentSpec, ScriptMatcher, type ScriptParameter, type ScriptRecipe, type ScriptRecipeMetaRow, type ScriptRecipeStore, type ScriptResult, ScriptStore, type ScriptStoreOptions, type SelectedWorkflow, type ShimDef, type ShimSummary, type ShimTarget, type SourcePolicy, type SpecToValidate, SuperatomSDK, type SuperatomSDKConfig, type T_RESPONSE, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, type TriggerEvent, type TriggerFireInfo, UIBlock, UILogCollector, type User, UserManager, type UsersData, type ValidationContext, type ValidationFinding, type WorkflowDescriptor, type Workspace, accessFromConfig, analyticsClient, answerCatalogForBinding, answerComponents, anthropicLLM, assertReadOnlySql, bindContext, bridgePreamble, captureContext, clearContextTokens, constraintPoliciesFromConfig, contextTokenCount, createCodexSession, createCodingAgentSession, dashboardConversationHistory, dataFingerprint, describeContext, describeTriggers, emitAgentEvent, extractNamedExport, formatFindingsForAgent, geminiLLM, getCodingAgent, getCodingAgentsConfig, getPiSessionState, getRequestContext, groqLLM, hybridRerank, inferColumns, isUserContext, listCodingAgents, llmUsageLogger, logger, makeFmt, mintContextToken, normalizeScriptBody, onAgentRun, onPiSessionUpdate, openaiLLM, policiesFromConfig, projectForDatasetBinding, promptLoader, queryCache, registerCodingAgentCollection, registerCodingAgents, registerDashboardAgentCollection, requireRequestContext, rerankChromaResults, rerankConversationResults, resolveContextToken, resolveScriptRecipeStore, revokeContextToken, runAsSystem, runAsUser, runCodingAgent, runScript, runWithContext, runWithMessageIdentity, startAgentTriggers, stopAgentTriggers, userPromptErrorLogger, validateComponentSpec, withCapturedContext, writeBridgeShims, writeShims };
package/dist/index.d.ts CHANGED
@@ -4480,6 +4480,56 @@ interface SourcePolicy {
4480
4480
  /** Grants outside the scope above. Empty or absent means none. */
4481
4481
  exceptions?: PolicyException[];
4482
4482
  }
4483
+ /**
4484
+ * Operators the constraint engine builds predicates from. A superset of
4485
+ * {@link RowFilter.op}, which the original rewriter is limited to.
4486
+ */
4487
+ type ConstraintOp = 'in' | 'not_in' | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'not_like' | 'is_null' | 'is_not_null';
4488
+ /**
4489
+ * One filter in a {@link ConstraintPolicy}.
4490
+ *
4491
+ * No `table`, unlike {@link RowFilter}: these are keyed by table, so a filter
4492
+ * cannot fail to match the table it was written for and leave it unfiltered.
4493
+ */
4494
+ interface ConstraintFilter {
4495
+ column: string;
4496
+ op?: ConstraintOp;
4497
+ /** Omitted only for `is_null` / `is_not_null`. */
4498
+ values?: unknown[];
4499
+ label?: string;
4500
+ }
4501
+ /**
4502
+ * What one user may see in one source, in the shape the constraint engine reads
4503
+ * (`sqlrewrite/v2/engine.py`).
4504
+ *
4505
+ * Not a superset of {@link SourcePolicy} but a different shape: it adds
4506
+ * schemas, columns and limits. A claim carries one shape or the other, never
4507
+ * both.
4508
+ */
4509
+ interface ConstraintPolicy {
4510
+ sourceId?: string | null;
4511
+ label?: string;
4512
+ /**
4513
+ * Every table is read from this schema, whatever schema the query wrote —
4514
+ * which is what makes it a restriction: there is no other schema to reach.
4515
+ * Absent means schemas are left exactly as written.
4516
+ */
4517
+ schema?: string;
4518
+ /** table → readable columns, or `null` for every column. Absent: every table. */
4519
+ readableTables?: Record<string, string[] | null>;
4520
+ /** table → filters, ANDed together. */
4521
+ rowFilters?: Record<string, ConstraintFilter[]>;
4522
+ limits?: {
4523
+ maxRows?: number;
4524
+ maxExecutionSeconds?: number;
4525
+ };
4526
+ /**
4527
+ * Why this policy could not be read. Set rather than dropping the policy:
4528
+ * a dropped policy leaves its source UNRESTRICTED, which is the opposite of
4529
+ * what a malformed restriction should mean. The gate refuses the source.
4530
+ */
4531
+ invalid?: string;
4532
+ }
4483
4533
  interface RequestContext {
4484
4534
  principal: Principal;
4485
4535
  projectId?: string;
@@ -4489,6 +4539,11 @@ interface RequestContext {
4489
4539
  * — see the backend's policy-gate.
4490
4540
  */
4491
4541
  policies?: SourcePolicy[];
4542
+ /**
4543
+ * The same restrictions in the constraint shape. Present only when the claim
4544
+ * said `version: 2`, and then `policies` is empty: one claim, one engine.
4545
+ */
4546
+ constraintPolicies?: ConstraintPolicy[];
4492
4547
  /**
4493
4548
  * The data sources this user may reach AT ALL, by source id.
4494
4549
  *
@@ -4541,6 +4596,7 @@ declare function runAsUser<T>(user: {
4541
4596
  projectId?: string;
4542
4597
  requestId?: string;
4543
4598
  policies?: SourcePolicy[];
4599
+ constraintPolicies?: ConstraintPolicy[];
4544
4600
  allowedSources?: string[] | null;
4545
4601
  graphVersion?: GraphVersion;
4546
4602
  }, fn: () => T): T;
@@ -4589,6 +4645,35 @@ declare function describeContext(ctx?: RequestContext | undefined): string;
4589
4645
  /** True when the context represents a signed-in user rather than a trusted job. */
4590
4646
  declare function isUserContext(ctx?: RequestContext | undefined): boolean;
4591
4647
 
4648
+ /**
4649
+ * The constraint policy shape (`version: 2`), read onto the request context.
4650
+ *
4651
+ * Strict, and failing CLOSED, which is the opposite of the legacy parser beside
4652
+ * it. There, dropping something malformed grants LESS. Here it grants MORE: a
4653
+ * dropped policy leaves its source with no restriction at all. So a policy that
4654
+ * cannot be read is kept, marked `invalid`, and the gate refuses the source.
4655
+ */
4656
+
4657
+ declare function constraintPoliciesFromConfig(raw: unknown): ConstraintPolicy[];
4658
+
4659
+ /**
4660
+ * The original policy shape, read out of the admin-authored per-user config.
4661
+ *
4662
+ * Every step here is defensive and DROPS what it cannot read, because the
4663
+ * config is free-form JSON typed by a human into an admin field. That is a
4664
+ * deliberate trade with a sharp edge — a typo'd policy silently becomes no
4665
+ * policy, i.e. unrestricted. The constraint shape next door takes the opposite
4666
+ * approach for exactly that reason.
4667
+ */
4668
+
4669
+ /**
4670
+ * Read access policies out of the admin-authored per-user config.
4671
+ *
4672
+ * Accepts either a single object or an array, so one user can be scoped across
4673
+ * several sources — and the object form, whose policies sit under `policies`.
4674
+ */
4675
+ declare function policiesFromConfig(config: unknown): SourcePolicy[];
4676
+
4592
4677
  /**
4593
4678
  * Deriving "who is asking" from an incoming WebSocket message.
4594
4679
  *
@@ -4603,6 +4688,8 @@ declare function isUserContext(ctx?: RequestContext | undefined): boolean;
4603
4688
  /** Everything one request's config says about what the caller may see. */
4604
4689
  interface ResolvedAccess {
4605
4690
  policies: SourcePolicy[];
4691
+ /** Set instead of `policies` when the claim said `version: 2`. */
4692
+ constraintPolicies: ConstraintPolicy[];
4606
4693
  /** `null` means every source; `[]` means none — see `RequestContext.allowedSources`. */
4607
4694
  allowedSources: string[] | null;
4608
4695
  }
@@ -4616,19 +4703,6 @@ interface ResolvedAccess {
4616
4703
  * drop EVERY row filter the customer wrote.
4617
4704
  */
4618
4705
  declare function accessFromConfig(config: unknown): ResolvedAccess;
4619
- /**
4620
- * Read access policies out of the admin-authored per-user config.
4621
- *
4622
- * The config is free-form JSON typed by a human into an admin field, so every
4623
- * step here is defensive: a malformed entry is DROPPED, never guessed at. That
4624
- * is a deliberate trade-off with a sharp edge — a typo'd policy silently
4625
- * becomes no policy, i.e. unrestricted. Validating shapes against the source
4626
- * schema (and refusing, rather than dropping) is the fix, and is not built yet.
4627
- *
4628
- * Accepts either a single object or an array, so one user can be scoped across
4629
- * several sources — and the object form, whose policies sit under `policies`.
4630
- */
4631
- declare function policiesFromConfig(config: unknown): SourcePolicy[];
4632
4706
  /**
4633
4707
  * Run `fn` with the caller's identity established.
4634
4708
  *
@@ -5238,7 +5312,8 @@ interface CodingAgentDefinition<TInput = unknown, TOutput = unknown> {
5238
5312
  promptDir?: string;
5239
5313
  /** Override the deployment default for this agent alone. */
5240
5314
  harness?: Harness;
5241
- model?: string;
5315
+ /** A function picks the model per run (e.g. by what the question carries); undefined = default. */
5316
+ model?: string | ((input: TInput, ctx: AgentRunContext) => string | undefined);
5242
5317
  /**
5243
5318
  * Let this agent's shell reach the network. Off by default.
5244
5319
  *
@@ -5454,13 +5529,6 @@ interface RunAgentOptions<TInput> extends AgentRunContext {
5454
5529
  */
5455
5530
  chat?: boolean;
5456
5531
  }
5457
- /**
5458
- * Run one agent, end to end.
5459
- *
5460
- * Never throws for an agent-side failure — a bad run comes back as a record
5461
- * with status 'error', because callers are usually crons and message handlers
5462
- * that must keep going. Genuinely programmer errors (unknown agent id) do throw.
5463
- */
5464
5532
  declare function runCodingAgent<TInput = unknown, TOutput = unknown>(agentId: string, options?: RunAgentOptions<TInput>): Promise<AgentRunRecord<TOutput>>;
5465
5533
 
5466
5534
  /**
@@ -6085,4 +6153,4 @@ declare class SuperatomSDK {
6085
6153
  getConversationSimilarityThreshold(): number;
6086
6154
  }
6087
6155
 
6088
- export { ALL_COLUMN_KEYS, ANSWER_COMPONENT_NAMES, type AgentConfig, type AgentEvent, type AgentResponse, type AgentRunContext, type AgentRunHandlers, type AgentRunRecord, type AgentRunResult, type AgentTrigger, type AgentUsage, type AnalyticsEventType, type AnalyticsStatus, type AnswerProvider, type AnswerProviderContext, type ApplySummary, BM25L, type BM25LOptions, type BaseLLMConfig, type BuildTriggerInput, CATEGORY_KEYS, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CodexSessionOptions, type CodingAgentCollectionConfig, type CodingAgentDefinition, type CodingAgentSession, type CodingAgentSessionOptions, type CodingAgentsConfig, type ColInfo, type ColType, type CollectionHandler, type CollectionOperation, type Component, type DBUIBlock, DEFAULT_AGENT_CONFIG, DEFAULT_FMT_CONFIG, type DashboardAgentCollectionConfig, type DatabaseType, type FindingCode, type Fmt, type FmtConfig, type GeneratedFile, type GraphVersion, type Harness, type HybridSearchOptions, INSTRUCTIONS_FILE, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type McpServerSpec, type Message, MissingRequestContextError, type ModelStrategy, NUMERIC_KEYS, type NarrationUpdate, type OutputContract, type OutputField, type OutputValidator, type PiSessionListener, type PiSessionState, type PiTranscriptEntry, type PolicyException, type PolicyExceptionTable, type Principal, type PromptAttachment, type PromptContext, type RequestContext, type RerankedResult, type ResolvedAccess, type RowFilter, type RunAgentOptions, STORAGE_CONFIG, type ScriptComponentSpec, ScriptMatcher, type ScriptParameter, type ScriptRecipe, type ScriptRecipeMetaRow, type ScriptRecipeStore, type ScriptResult, ScriptStore, type ScriptStoreOptions, type SelectedWorkflow, type ShimDef, type ShimSummary, type ShimTarget, type SourcePolicy, type SpecToValidate, SuperatomSDK, type SuperatomSDKConfig, type T_RESPONSE, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, type TriggerEvent, type TriggerFireInfo, UIBlock, UILogCollector, type User, UserManager, type UsersData, type ValidationContext, type ValidationFinding, type WorkflowDescriptor, type Workspace, accessFromConfig, analyticsClient, answerCatalogForBinding, answerComponents, anthropicLLM, assertReadOnlySql, bindContext, bridgePreamble, captureContext, clearContextTokens, contextTokenCount, createCodexSession, createCodingAgentSession, dashboardConversationHistory, dataFingerprint, describeContext, describeTriggers, emitAgentEvent, extractNamedExport, formatFindingsForAgent, geminiLLM, getCodingAgent, getCodingAgentsConfig, getPiSessionState, getRequestContext, groqLLM, hybridRerank, inferColumns, isUserContext, listCodingAgents, llmUsageLogger, logger, makeFmt, mintContextToken, normalizeScriptBody, onAgentRun, onPiSessionUpdate, openaiLLM, policiesFromConfig, projectForDatasetBinding, promptLoader, queryCache, registerCodingAgentCollection, registerCodingAgents, registerDashboardAgentCollection, requireRequestContext, rerankChromaResults, rerankConversationResults, resolveContextToken, resolveScriptRecipeStore, revokeContextToken, runAsSystem, runAsUser, runCodingAgent, runScript, runWithContext, runWithMessageIdentity, startAgentTriggers, stopAgentTriggers, userPromptErrorLogger, validateComponentSpec, withCapturedContext, writeBridgeShims, writeShims };
6156
+ export { ALL_COLUMN_KEYS, ANSWER_COMPONENT_NAMES, type AgentConfig, type AgentEvent, type AgentResponse, type AgentRunContext, type AgentRunHandlers, type AgentRunRecord, type AgentRunResult, type AgentTrigger, type AgentUsage, type AnalyticsEventType, type AnalyticsStatus, type AnswerProvider, type AnswerProviderContext, type ApplySummary, BM25L, type BM25LOptions, type BaseLLMConfig, type BuildTriggerInput, CATEGORY_KEYS, CONTEXT_CONFIG, type CapturedLog, CleanupService, type CodexSessionOptions, type CodingAgentCollectionConfig, type CodingAgentDefinition, type CodingAgentSession, type CodingAgentSessionOptions, type CodingAgentsConfig, type ColInfo, type ColType, type CollectionHandler, type CollectionOperation, type Component, type ConstraintFilter, type ConstraintOp, type ConstraintPolicy, type DBUIBlock, DEFAULT_AGENT_CONFIG, DEFAULT_FMT_CONFIG, type DashboardAgentCollectionConfig, type DatabaseType, type FindingCode, type Fmt, type FmtConfig, type GeneratedFile, type GraphVersion, type Harness, type HybridSearchOptions, INSTRUCTIONS_FILE, type IncomingMessage, type KbNodesQueryFilters, type KbNodesRequestPayload, LLM, type LLMUsageEntry, type LogLevel, MainAgent, type McpServerSpec, type Message, MissingRequestContextError, type ModelStrategy, NUMERIC_KEYS, type NarrationUpdate, type OutputContract, type OutputField, type OutputValidator, type PiSessionListener, type PiSessionState, type PiTranscriptEntry, type PolicyException, type PolicyExceptionTable, type Principal, type PromptAttachment, type PromptContext, type RequestContext, type RerankedResult, type ResolvedAccess, type RowFilter, type RunAgentOptions, STORAGE_CONFIG, type ScriptComponentSpec, ScriptMatcher, type ScriptParameter, type ScriptRecipe, type ScriptRecipeMetaRow, type ScriptRecipeStore, type ScriptResult, ScriptStore, type ScriptStoreOptions, type SelectedWorkflow, type ShimDef, type ShimSummary, type ShimTarget, type SourcePolicy, type SpecToValidate, SuperatomSDK, type SuperatomSDKConfig, type T_RESPONSE, type TaskType, Thread, ThreadManager, type Tool$1 as Tool, type ToolOutputSchema, type TriggerEvent, type TriggerFireInfo, UIBlock, UILogCollector, type User, UserManager, type UsersData, type ValidationContext, type ValidationFinding, type WorkflowDescriptor, type Workspace, accessFromConfig, analyticsClient, answerCatalogForBinding, answerComponents, anthropicLLM, assertReadOnlySql, bindContext, bridgePreamble, captureContext, clearContextTokens, constraintPoliciesFromConfig, contextTokenCount, createCodexSession, createCodingAgentSession, dashboardConversationHistory, dataFingerprint, describeContext, describeTriggers, emitAgentEvent, extractNamedExport, formatFindingsForAgent, geminiLLM, getCodingAgent, getCodingAgentsConfig, getPiSessionState, getRequestContext, groqLLM, hybridRerank, inferColumns, isUserContext, listCodingAgents, llmUsageLogger, logger, makeFmt, mintContextToken, normalizeScriptBody, onAgentRun, onPiSessionUpdate, openaiLLM, policiesFromConfig, projectForDatasetBinding, promptLoader, queryCache, registerCodingAgentCollection, registerCodingAgents, registerDashboardAgentCollection, requireRequestContext, rerankChromaResults, rerankConversationResults, resolveContextToken, resolveScriptRecipeStore, revokeContextToken, runAsSystem, runAsUser, runCodingAgent, runScript, runWithContext, runWithMessageIdentity, startAgentTriggers, stopAgentTriggers, userPromptErrorLogger, validateComponentSpec, withCapturedContext, writeBridgeShims, writeShims };
package/dist/index.js CHANGED
@@ -62,6 +62,7 @@ __export(index_exports, {
62
62
  bridgePreamble: () => bridgePreamble,
63
63
  captureContext: () => captureContext,
64
64
  clearContextTokens: () => clearContextTokens,
65
+ constraintPoliciesFromConfig: () => constraintPoliciesFromConfig,
65
66
  contextTokenCount: () => contextTokenCount,
66
67
  createCodexSession: () => createCodexSession,
67
68
  createCodingAgentSession: () => createCodingAgentSession,
@@ -1952,7 +1953,17 @@ function runWithContext(ctx, fn) {
1952
1953
  return getStorage().run(ctx, fn);
1953
1954
  }
1954
1955
  function runAsUser(user, fn) {
1955
- const { userId, orgId, role, projectId, requestId, policies, allowedSources, graphVersion } = user;
1956
+ const {
1957
+ userId,
1958
+ orgId,
1959
+ role,
1960
+ projectId,
1961
+ requestId,
1962
+ policies,
1963
+ constraintPolicies,
1964
+ allowedSources,
1965
+ graphVersion
1966
+ } = user;
1956
1967
  return runWithContext(
1957
1968
  // `allowedSources` is spread in only when given, so an absent restriction
1958
1969
  // stays absent rather than becoming an explicit `undefined` that a later
@@ -1962,6 +1973,7 @@ function runAsUser(user, fn) {
1962
1973
  projectId,
1963
1974
  requestId,
1964
1975
  policies,
1976
+ ...constraintPolicies?.length ? { constraintPolicies } : {},
1965
1977
  ...allowedSources !== void 0 ? { allowedSources } : {},
1966
1978
  ...graphVersion !== void 0 ? { graphVersion } : {}
1967
1979
  },
@@ -2642,42 +2654,148 @@ function buildDirectToolCacheKey(toolId, parameters) {
2642
2654
  return `et-direct:${toolId}:${stableStringify(parameters || {})}`;
2643
2655
  }
2644
2656
 
2645
- // src/handlers/request-identity.ts
2646
- function graphVersionOf(v) {
2647
- if (v === "draft") return "draft";
2648
- return Number.isInteger(v) && v > 0 ? v : void 0;
2657
+ // src/handlers/data-access/v2/policies.ts
2658
+ var CONSTRAINT_OPS = [
2659
+ "in",
2660
+ "not_in",
2661
+ "eq",
2662
+ "ne",
2663
+ "gt",
2664
+ "gte",
2665
+ "lt",
2666
+ "lte",
2667
+ "like",
2668
+ "not_like",
2669
+ "is_null",
2670
+ "is_not_null"
2671
+ ];
2672
+ var NO_VALUE_OPS = /* @__PURE__ */ new Set(["is_null", "is_not_null"]);
2673
+ function constraintPoliciesFromConfig(raw) {
2674
+ const entries = Array.isArray(raw) ? raw : [raw];
2675
+ const out = [];
2676
+ for (const entry of entries) {
2677
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
2678
+ const e = entry;
2679
+ const sourceId = typeof e.sourceId === "string" && e.sourceId ? e.sourceId : null;
2680
+ const label = typeof e.label === "string" && e.label.trim() ? e.label.trim() : void 0;
2681
+ const bad = (why) => ({ sourceId, label, invalid: why });
2682
+ let schema2;
2683
+ if (e.schema !== void 0 && e.schema !== null) {
2684
+ if (typeof e.schema !== "string" || !e.schema.trim()) {
2685
+ out.push(bad("schema must be a schema name"));
2686
+ continue;
2687
+ }
2688
+ schema2 = e.schema.trim();
2689
+ }
2690
+ let readableTables;
2691
+ let tablesBad;
2692
+ if (e.readableTables !== void 0 && e.readableTables !== null) {
2693
+ if (typeof e.readableTables !== "object" || Array.isArray(e.readableTables)) {
2694
+ out.push(bad("readableTables must be an object of table to columns"));
2695
+ continue;
2696
+ }
2697
+ readableTables = {};
2698
+ for (const [table, cols] of Object.entries(e.readableTables)) {
2699
+ if (cols === null) {
2700
+ readableTables[table] = null;
2701
+ continue;
2702
+ }
2703
+ if (!Array.isArray(cols) || cols.length === 0) {
2704
+ tablesBad = `readableTables["${table}"] must be a non-empty list or null`;
2705
+ break;
2706
+ }
2707
+ const columns = cols.filter((c) => typeof c === "string" && !!c.trim()).map((c) => c.trim());
2708
+ if (columns.length !== cols.length) {
2709
+ tablesBad = `readableTables["${table}"] has an entry that is not a column name`;
2710
+ break;
2711
+ }
2712
+ readableTables[table] = columns;
2713
+ }
2714
+ if (!tablesBad && Object.keys(readableTables).length === 0) {
2715
+ tablesBad = "readableTables names no tables \u2014 omit it to allow every table";
2716
+ }
2717
+ if (tablesBad) {
2718
+ out.push(bad(tablesBad));
2719
+ continue;
2720
+ }
2721
+ }
2722
+ const rowFilters = {};
2723
+ let filtersBad;
2724
+ const rawFilters = e.rowFilters;
2725
+ if (rawFilters !== void 0 && rawFilters !== null) {
2726
+ if (typeof rawFilters !== "object" || Array.isArray(rawFilters)) {
2727
+ filtersBad = "rowFilters must be an object keyed by table";
2728
+ } else {
2729
+ for (const [table, items] of Object.entries(rawFilters)) {
2730
+ if (readableTables && !(table in readableTables)) {
2731
+ filtersBad = `rowFilters names "${table}", which readableTables does not allow`;
2732
+ break;
2733
+ }
2734
+ if (!Array.isArray(items) || items.length === 0) {
2735
+ filtersBad = `rowFilters["${table}"] must be a non-empty list`;
2736
+ break;
2737
+ }
2738
+ const parsed = [];
2739
+ for (const item of items) {
2740
+ if (!item || typeof item !== "object") {
2741
+ filtersBad = `rowFilters["${table}"] has an entry that is not an object`;
2742
+ break;
2743
+ }
2744
+ const f = item;
2745
+ if (typeof f.column !== "string" || !f.column.trim()) {
2746
+ filtersBad = `a filter on "${table}" has no column`;
2747
+ break;
2748
+ }
2749
+ const op = typeof f.op === "string" ? f.op.toLowerCase() : "in";
2750
+ if (!CONSTRAINT_OPS.includes(op)) {
2751
+ filtersBad = `unsupported operator "${f.op}" on "${table}.${f.column}"`;
2752
+ break;
2753
+ }
2754
+ const values = Array.isArray(f.values) ? f.values : [];
2755
+ if (!NO_VALUE_OPS.has(op) && values.length === 0) {
2756
+ filtersBad = `the filter on "${table}.${f.column}" has no values`;
2757
+ break;
2758
+ }
2759
+ parsed.push({
2760
+ column: f.column.trim(),
2761
+ op,
2762
+ ...NO_VALUE_OPS.has(op) ? {} : { values },
2763
+ label: typeof f.label === "string" && f.label.trim() ? f.label.trim() : void 0
2764
+ });
2765
+ }
2766
+ if (filtersBad) break;
2767
+ rowFilters[table] = parsed;
2768
+ }
2769
+ }
2770
+ }
2771
+ if (filtersBad) {
2772
+ out.push(bad(filtersBad));
2773
+ continue;
2774
+ }
2775
+ const rawLimits = e.limits;
2776
+ const num = (v) => typeof v === "number" && Number.isInteger(v) && v > 0 ? v : void 0;
2777
+ const limits = rawLimits ? { maxRows: num(rawLimits.maxRows), maxExecutionSeconds: num(rawLimits.maxExecutionSeconds) } : void 0;
2778
+ out.push({
2779
+ sourceId,
2780
+ label,
2781
+ ...schema2 ? { schema: schema2 } : {},
2782
+ ...readableTables ? { readableTables } : {},
2783
+ ...Object.keys(rowFilters).length ? { rowFilters } : {},
2784
+ ...limits ? { limits } : {}
2785
+ });
2786
+ }
2787
+ return out;
2649
2788
  }
2789
+
2790
+ // src/handlers/data-access/shape.ts
2650
2791
  function scopedForm(config2) {
2651
2792
  if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) return void 0;
2652
2793
  const c = config2;
2653
2794
  return "allowedSources" in c || "policies" in c ? c : void 0;
2654
2795
  }
2655
- function allowedSourcesFrom(value) {
2656
- if (value === void 0 || value === null) return null;
2657
- if (!Array.isArray(value)) {
2658
- if (!warnedBadSources) {
2659
- warnedBadSources = true;
2660
- logger.warn(
2661
- `[RequestIdentity] "allowedSources" is ${typeof value}, not a list of source ids \u2014 treating it as NO sources rather than all of them. Fix the config: "allowedSources": ["mssql-1a2b"].`
2662
- );
2663
- }
2664
- return [];
2665
- }
2666
- const out = [];
2667
- for (const item of value) {
2668
- if (typeof item !== "string" || !item.trim()) continue;
2669
- const id = item.trim();
2670
- if (!out.includes(id)) out.push(id);
2671
- }
2672
- return out;
2673
- }
2674
- function accessFromConfig(config2) {
2675
- const scoped = scopedForm(config2);
2676
- return {
2677
- policies: policiesFromConfig(config2),
2678
- allowedSources: scoped ? allowedSourcesFrom(scoped.allowedSources) : null
2679
- };
2680
- }
2796
+
2797
+ // src/handlers/data-access/v1/policies.ts
2798
+ var warnedUnlabelled = false;
2681
2799
  function exceptionsFromConfig(raw) {
2682
2800
  if (!Array.isArray(raw)) return [];
2683
2801
  const str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
@@ -2756,16 +2874,55 @@ function policiesFromConfig(config2) {
2756
2874
  }
2757
2875
  return out;
2758
2876
  }
2877
+
2878
+ // src/handlers/request-identity.ts
2879
+ function graphVersionOf(v) {
2880
+ if (v === "draft") return "draft";
2881
+ return Number.isInteger(v) && v > 0 ? v : void 0;
2882
+ }
2883
+ function allowedSourcesFrom(value) {
2884
+ if (value === void 0 || value === null) return null;
2885
+ if (!Array.isArray(value)) {
2886
+ if (!warnedBadSources) {
2887
+ warnedBadSources = true;
2888
+ logger.warn(
2889
+ `[RequestIdentity] "allowedSources" is ${typeof value}, not a list of source ids \u2014 treating it as NO sources rather than all of them. Fix the config: "allowedSources": ["mssql-1a2b"].`
2890
+ );
2891
+ }
2892
+ return [];
2893
+ }
2894
+ const out = [];
2895
+ for (const item of value) {
2896
+ if (typeof item !== "string" || !item.trim()) continue;
2897
+ const id = item.trim();
2898
+ if (!out.includes(id)) out.push(id);
2899
+ }
2900
+ return out;
2901
+ }
2902
+ function accessFromConfig(config2) {
2903
+ const scoped = scopedForm(config2);
2904
+ const allowedSources = scoped ? allowedSourcesFrom(scoped.allowedSources) : null;
2905
+ if (scoped?.version === 2) {
2906
+ return {
2907
+ policies: [],
2908
+ constraintPolicies: constraintPoliciesFromConfig(scoped.policies),
2909
+ allowedSources
2910
+ };
2911
+ }
2912
+ return { policies: policiesFromConfig(config2), constraintPolicies: [], allowedSources };
2913
+ }
2759
2914
  var warnedUntrusted = false;
2760
2915
  var warnedAnonymous = false;
2761
- var warnedUnlabelled = false;
2762
2916
  var warnedBadSources = false;
2763
2917
  var announcedVerified = false;
2764
2918
  var reportedIdMismatches = /* @__PURE__ */ new Set();
2765
2919
  function runWithMessageIdentity(message, fn) {
2766
2920
  const verified = message?.authContext;
2767
2921
  const requestId = message?.id;
2768
- const { policies, allowedSources } = verified ? accessFromConfig(verified.config) : accessFromConfig(message?.payload?.config);
2922
+ const { policies, constraintPolicies, allowedSources } = verified ? accessFromConfig(verified.config) : accessFromConfig(message?.payload?.config);
2923
+ logger.info(
2924
+ `[RequestIdentity] ${message?.type ?? "?"}: access from ${verified ? "authContext" : "payload"}${verified && verified.config == null ? " (none on it)" : ""}, allowedSources=${allowedSources === null ? "every source" : `[${allowedSources.join(", ") || "NONE"}]`}, ${constraintPolicies.length} constraint policy(ies), ${policies.length} row policy(ies)`
2925
+ );
2769
2926
  if (verified?.userId) {
2770
2927
  const claimedId = message?.payload?.userId;
2771
2928
  if (!announcedVerified) {
@@ -2790,6 +2947,7 @@ function runWithMessageIdentity(message, fn) {
2790
2947
  role: verified.role,
2791
2948
  requestId,
2792
2949
  policies,
2950
+ constraintPolicies,
2793
2951
  // `?? undefined` so "no restriction" leaves the key off the context
2794
2952
  // entirely; `[]` is passed through as the deny-everything it means.
2795
2953
  allowedSources: allowedSources ?? void 0,
@@ -2807,7 +2965,10 @@ function runWithMessageIdentity(message, fn) {
2807
2965
  "[RequestIdentity] Using the client-supplied payload.userId as the principal \u2014 the relay is not stamping a verified authContext yet, so this identity is NOT trustworthy. Fine for plumbing; do not enable policy enforcement until the relay change lands."
2808
2966
  );
2809
2967
  }
2810
- return runAsUser({ userId: claimed, requestId, policies, allowedSources: allowedSources ?? void 0 }, fn);
2968
+ return runAsUser(
2969
+ { userId: claimed, requestId, policies, constraintPolicies, allowedSources: allowedSources ?? void 0 },
2970
+ fn
2971
+ );
2811
2972
  }
2812
2973
  if (!warnedAnonymous) {
2813
2974
  warnedAnonymous = true;
@@ -25422,6 +25583,9 @@ async function pruneRuns(agentDir, keep) {
25422
25583
  } catch {
25423
25584
  }
25424
25585
  }
25586
+ function runModel(def, input, ctx, fallback) {
25587
+ return (typeof def.model === "function" ? def.model(input, ctx) : def.model) || fallback;
25588
+ }
25425
25589
  async function runCodingAgent(agentId, options = {}) {
25426
25590
  const def = getCodingAgent(agentId);
25427
25591
  if (!def) {
@@ -25513,9 +25677,10 @@ async function runCodingAgent(agentId, options = {}) {
25513
25677
  (runAsJob && def.buildPrompt ? await def.buildPrompt(ws, input, ctx) : outputPath ? `Carry out the task described in ${INSTRUCTIONS_FILE}. Write your finished result to this exact absolute path: ${outputPath}` : `Carry out the task described in ${INSTRUCTIONS_FILE}.`);
25514
25678
  const tools = { ...cfg.sharedTools ?? {}, ...def.tools ?? {} };
25515
25679
  const sessionEnv = def.env ? await def.env(input, ctx) : void 0;
25680
+ const model = runModel(def, input, ctx, cfg.model);
25516
25681
  const session = createCodingAgentSession(def.harness || cfg.harness, {
25517
25682
  cwd: dir,
25518
- model: def.model || cfg.model,
25683
+ model,
25519
25684
  resumeId,
25520
25685
  instructionsFile: INSTRUCTIONS_FILE,
25521
25686
  // undefined, not {}, when this agent has no tools — see the driver.
@@ -25602,7 +25767,7 @@ async function runCodingAgent(agentId, options = {}) {
25602
25767
  logger.warn(`[CodingAgent:${def.id}] deliverable rejected (${message}) \u2014 asking the agent to repair it`);
25603
25768
  const repair = createCodingAgentSession(def.harness || cfg.harness, {
25604
25769
  cwd: dir,
25605
- model: def.model || cfg.model,
25770
+ model,
25606
25771
  resumeId: result.sessionId,
25607
25772
  instructionsFile: INSTRUCTIONS_FILE,
25608
25773
  mcpServers: tools && Object.keys(tools).length ? tools : void 0,
@@ -30205,6 +30370,7 @@ var SuperatomSDK = class {
30205
30370
  bridgePreamble,
30206
30371
  captureContext,
30207
30372
  clearContextTokens,
30373
+ constraintPoliciesFromConfig,
30208
30374
  contextTokenCount,
30209
30375
  createCodexSession,
30210
30376
  createCodingAgentSession,
package/dist/index.mjs CHANGED
@@ -1840,7 +1840,17 @@ function runWithContext(ctx, fn) {
1840
1840
  return getStorage().run(ctx, fn);
1841
1841
  }
1842
1842
  function runAsUser(user, fn) {
1843
- const { userId, orgId, role, projectId, requestId, policies, allowedSources, graphVersion } = user;
1843
+ const {
1844
+ userId,
1845
+ orgId,
1846
+ role,
1847
+ projectId,
1848
+ requestId,
1849
+ policies,
1850
+ constraintPolicies,
1851
+ allowedSources,
1852
+ graphVersion
1853
+ } = user;
1844
1854
  return runWithContext(
1845
1855
  // `allowedSources` is spread in only when given, so an absent restriction
1846
1856
  // stays absent rather than becoming an explicit `undefined` that a later
@@ -1850,6 +1860,7 @@ function runAsUser(user, fn) {
1850
1860
  projectId,
1851
1861
  requestId,
1852
1862
  policies,
1863
+ ...constraintPolicies?.length ? { constraintPolicies } : {},
1853
1864
  ...allowedSources !== void 0 ? { allowedSources } : {},
1854
1865
  ...graphVersion !== void 0 ? { graphVersion } : {}
1855
1866
  },
@@ -2530,42 +2541,148 @@ function buildDirectToolCacheKey(toolId, parameters) {
2530
2541
  return `et-direct:${toolId}:${stableStringify(parameters || {})}`;
2531
2542
  }
2532
2543
 
2533
- // src/handlers/request-identity.ts
2534
- function graphVersionOf(v) {
2535
- if (v === "draft") return "draft";
2536
- return Number.isInteger(v) && v > 0 ? v : void 0;
2544
+ // src/handlers/data-access/v2/policies.ts
2545
+ var CONSTRAINT_OPS = [
2546
+ "in",
2547
+ "not_in",
2548
+ "eq",
2549
+ "ne",
2550
+ "gt",
2551
+ "gte",
2552
+ "lt",
2553
+ "lte",
2554
+ "like",
2555
+ "not_like",
2556
+ "is_null",
2557
+ "is_not_null"
2558
+ ];
2559
+ var NO_VALUE_OPS = /* @__PURE__ */ new Set(["is_null", "is_not_null"]);
2560
+ function constraintPoliciesFromConfig(raw) {
2561
+ const entries = Array.isArray(raw) ? raw : [raw];
2562
+ const out = [];
2563
+ for (const entry of entries) {
2564
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
2565
+ const e = entry;
2566
+ const sourceId = typeof e.sourceId === "string" && e.sourceId ? e.sourceId : null;
2567
+ const label = typeof e.label === "string" && e.label.trim() ? e.label.trim() : void 0;
2568
+ const bad = (why) => ({ sourceId, label, invalid: why });
2569
+ let schema2;
2570
+ if (e.schema !== void 0 && e.schema !== null) {
2571
+ if (typeof e.schema !== "string" || !e.schema.trim()) {
2572
+ out.push(bad("schema must be a schema name"));
2573
+ continue;
2574
+ }
2575
+ schema2 = e.schema.trim();
2576
+ }
2577
+ let readableTables;
2578
+ let tablesBad;
2579
+ if (e.readableTables !== void 0 && e.readableTables !== null) {
2580
+ if (typeof e.readableTables !== "object" || Array.isArray(e.readableTables)) {
2581
+ out.push(bad("readableTables must be an object of table to columns"));
2582
+ continue;
2583
+ }
2584
+ readableTables = {};
2585
+ for (const [table, cols] of Object.entries(e.readableTables)) {
2586
+ if (cols === null) {
2587
+ readableTables[table] = null;
2588
+ continue;
2589
+ }
2590
+ if (!Array.isArray(cols) || cols.length === 0) {
2591
+ tablesBad = `readableTables["${table}"] must be a non-empty list or null`;
2592
+ break;
2593
+ }
2594
+ const columns = cols.filter((c) => typeof c === "string" && !!c.trim()).map((c) => c.trim());
2595
+ if (columns.length !== cols.length) {
2596
+ tablesBad = `readableTables["${table}"] has an entry that is not a column name`;
2597
+ break;
2598
+ }
2599
+ readableTables[table] = columns;
2600
+ }
2601
+ if (!tablesBad && Object.keys(readableTables).length === 0) {
2602
+ tablesBad = "readableTables names no tables \u2014 omit it to allow every table";
2603
+ }
2604
+ if (tablesBad) {
2605
+ out.push(bad(tablesBad));
2606
+ continue;
2607
+ }
2608
+ }
2609
+ const rowFilters = {};
2610
+ let filtersBad;
2611
+ const rawFilters = e.rowFilters;
2612
+ if (rawFilters !== void 0 && rawFilters !== null) {
2613
+ if (typeof rawFilters !== "object" || Array.isArray(rawFilters)) {
2614
+ filtersBad = "rowFilters must be an object keyed by table";
2615
+ } else {
2616
+ for (const [table, items] of Object.entries(rawFilters)) {
2617
+ if (readableTables && !(table in readableTables)) {
2618
+ filtersBad = `rowFilters names "${table}", which readableTables does not allow`;
2619
+ break;
2620
+ }
2621
+ if (!Array.isArray(items) || items.length === 0) {
2622
+ filtersBad = `rowFilters["${table}"] must be a non-empty list`;
2623
+ break;
2624
+ }
2625
+ const parsed = [];
2626
+ for (const item of items) {
2627
+ if (!item || typeof item !== "object") {
2628
+ filtersBad = `rowFilters["${table}"] has an entry that is not an object`;
2629
+ break;
2630
+ }
2631
+ const f = item;
2632
+ if (typeof f.column !== "string" || !f.column.trim()) {
2633
+ filtersBad = `a filter on "${table}" has no column`;
2634
+ break;
2635
+ }
2636
+ const op = typeof f.op === "string" ? f.op.toLowerCase() : "in";
2637
+ if (!CONSTRAINT_OPS.includes(op)) {
2638
+ filtersBad = `unsupported operator "${f.op}" on "${table}.${f.column}"`;
2639
+ break;
2640
+ }
2641
+ const values = Array.isArray(f.values) ? f.values : [];
2642
+ if (!NO_VALUE_OPS.has(op) && values.length === 0) {
2643
+ filtersBad = `the filter on "${table}.${f.column}" has no values`;
2644
+ break;
2645
+ }
2646
+ parsed.push({
2647
+ column: f.column.trim(),
2648
+ op,
2649
+ ...NO_VALUE_OPS.has(op) ? {} : { values },
2650
+ label: typeof f.label === "string" && f.label.trim() ? f.label.trim() : void 0
2651
+ });
2652
+ }
2653
+ if (filtersBad) break;
2654
+ rowFilters[table] = parsed;
2655
+ }
2656
+ }
2657
+ }
2658
+ if (filtersBad) {
2659
+ out.push(bad(filtersBad));
2660
+ continue;
2661
+ }
2662
+ const rawLimits = e.limits;
2663
+ const num = (v) => typeof v === "number" && Number.isInteger(v) && v > 0 ? v : void 0;
2664
+ const limits = rawLimits ? { maxRows: num(rawLimits.maxRows), maxExecutionSeconds: num(rawLimits.maxExecutionSeconds) } : void 0;
2665
+ out.push({
2666
+ sourceId,
2667
+ label,
2668
+ ...schema2 ? { schema: schema2 } : {},
2669
+ ...readableTables ? { readableTables } : {},
2670
+ ...Object.keys(rowFilters).length ? { rowFilters } : {},
2671
+ ...limits ? { limits } : {}
2672
+ });
2673
+ }
2674
+ return out;
2537
2675
  }
2676
+
2677
+ // src/handlers/data-access/shape.ts
2538
2678
  function scopedForm(config2) {
2539
2679
  if (!config2 || typeof config2 !== "object" || Array.isArray(config2)) return void 0;
2540
2680
  const c = config2;
2541
2681
  return "allowedSources" in c || "policies" in c ? c : void 0;
2542
2682
  }
2543
- function allowedSourcesFrom(value) {
2544
- if (value === void 0 || value === null) return null;
2545
- if (!Array.isArray(value)) {
2546
- if (!warnedBadSources) {
2547
- warnedBadSources = true;
2548
- logger.warn(
2549
- `[RequestIdentity] "allowedSources" is ${typeof value}, not a list of source ids \u2014 treating it as NO sources rather than all of them. Fix the config: "allowedSources": ["mssql-1a2b"].`
2550
- );
2551
- }
2552
- return [];
2553
- }
2554
- const out = [];
2555
- for (const item of value) {
2556
- if (typeof item !== "string" || !item.trim()) continue;
2557
- const id = item.trim();
2558
- if (!out.includes(id)) out.push(id);
2559
- }
2560
- return out;
2561
- }
2562
- function accessFromConfig(config2) {
2563
- const scoped = scopedForm(config2);
2564
- return {
2565
- policies: policiesFromConfig(config2),
2566
- allowedSources: scoped ? allowedSourcesFrom(scoped.allowedSources) : null
2567
- };
2568
- }
2683
+
2684
+ // src/handlers/data-access/v1/policies.ts
2685
+ var warnedUnlabelled = false;
2569
2686
  function exceptionsFromConfig(raw) {
2570
2687
  if (!Array.isArray(raw)) return [];
2571
2688
  const str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
@@ -2644,16 +2761,55 @@ function policiesFromConfig(config2) {
2644
2761
  }
2645
2762
  return out;
2646
2763
  }
2764
+
2765
+ // src/handlers/request-identity.ts
2766
+ function graphVersionOf(v) {
2767
+ if (v === "draft") return "draft";
2768
+ return Number.isInteger(v) && v > 0 ? v : void 0;
2769
+ }
2770
+ function allowedSourcesFrom(value) {
2771
+ if (value === void 0 || value === null) return null;
2772
+ if (!Array.isArray(value)) {
2773
+ if (!warnedBadSources) {
2774
+ warnedBadSources = true;
2775
+ logger.warn(
2776
+ `[RequestIdentity] "allowedSources" is ${typeof value}, not a list of source ids \u2014 treating it as NO sources rather than all of them. Fix the config: "allowedSources": ["mssql-1a2b"].`
2777
+ );
2778
+ }
2779
+ return [];
2780
+ }
2781
+ const out = [];
2782
+ for (const item of value) {
2783
+ if (typeof item !== "string" || !item.trim()) continue;
2784
+ const id = item.trim();
2785
+ if (!out.includes(id)) out.push(id);
2786
+ }
2787
+ return out;
2788
+ }
2789
+ function accessFromConfig(config2) {
2790
+ const scoped = scopedForm(config2);
2791
+ const allowedSources = scoped ? allowedSourcesFrom(scoped.allowedSources) : null;
2792
+ if (scoped?.version === 2) {
2793
+ return {
2794
+ policies: [],
2795
+ constraintPolicies: constraintPoliciesFromConfig(scoped.policies),
2796
+ allowedSources
2797
+ };
2798
+ }
2799
+ return { policies: policiesFromConfig(config2), constraintPolicies: [], allowedSources };
2800
+ }
2647
2801
  var warnedUntrusted = false;
2648
2802
  var warnedAnonymous = false;
2649
- var warnedUnlabelled = false;
2650
2803
  var warnedBadSources = false;
2651
2804
  var announcedVerified = false;
2652
2805
  var reportedIdMismatches = /* @__PURE__ */ new Set();
2653
2806
  function runWithMessageIdentity(message, fn) {
2654
2807
  const verified = message?.authContext;
2655
2808
  const requestId = message?.id;
2656
- const { policies, allowedSources } = verified ? accessFromConfig(verified.config) : accessFromConfig(message?.payload?.config);
2809
+ const { policies, constraintPolicies, allowedSources } = verified ? accessFromConfig(verified.config) : accessFromConfig(message?.payload?.config);
2810
+ logger.info(
2811
+ `[RequestIdentity] ${message?.type ?? "?"}: access from ${verified ? "authContext" : "payload"}${verified && verified.config == null ? " (none on it)" : ""}, allowedSources=${allowedSources === null ? "every source" : `[${allowedSources.join(", ") || "NONE"}]`}, ${constraintPolicies.length} constraint policy(ies), ${policies.length} row policy(ies)`
2812
+ );
2657
2813
  if (verified?.userId) {
2658
2814
  const claimedId = message?.payload?.userId;
2659
2815
  if (!announcedVerified) {
@@ -2678,6 +2834,7 @@ function runWithMessageIdentity(message, fn) {
2678
2834
  role: verified.role,
2679
2835
  requestId,
2680
2836
  policies,
2837
+ constraintPolicies,
2681
2838
  // `?? undefined` so "no restriction" leaves the key off the context
2682
2839
  // entirely; `[]` is passed through as the deny-everything it means.
2683
2840
  allowedSources: allowedSources ?? void 0,
@@ -2695,7 +2852,10 @@ function runWithMessageIdentity(message, fn) {
2695
2852
  "[RequestIdentity] Using the client-supplied payload.userId as the principal \u2014 the relay is not stamping a verified authContext yet, so this identity is NOT trustworthy. Fine for plumbing; do not enable policy enforcement until the relay change lands."
2696
2853
  );
2697
2854
  }
2698
- return runAsUser({ userId: claimed, requestId, policies, allowedSources: allowedSources ?? void 0 }, fn);
2855
+ return runAsUser(
2856
+ { userId: claimed, requestId, policies, constraintPolicies, allowedSources: allowedSources ?? void 0 },
2857
+ fn
2858
+ );
2699
2859
  }
2700
2860
  if (!warnedAnonymous) {
2701
2861
  warnedAnonymous = true;
@@ -25310,6 +25470,9 @@ async function pruneRuns(agentDir, keep) {
25310
25470
  } catch {
25311
25471
  }
25312
25472
  }
25473
+ function runModel(def, input, ctx, fallback) {
25474
+ return (typeof def.model === "function" ? def.model(input, ctx) : def.model) || fallback;
25475
+ }
25313
25476
  async function runCodingAgent(agentId, options = {}) {
25314
25477
  const def = getCodingAgent(agentId);
25315
25478
  if (!def) {
@@ -25401,9 +25564,10 @@ async function runCodingAgent(agentId, options = {}) {
25401
25564
  (runAsJob && def.buildPrompt ? await def.buildPrompt(ws, input, ctx) : outputPath ? `Carry out the task described in ${INSTRUCTIONS_FILE}. Write your finished result to this exact absolute path: ${outputPath}` : `Carry out the task described in ${INSTRUCTIONS_FILE}.`);
25402
25565
  const tools = { ...cfg.sharedTools ?? {}, ...def.tools ?? {} };
25403
25566
  const sessionEnv = def.env ? await def.env(input, ctx) : void 0;
25567
+ const model = runModel(def, input, ctx, cfg.model);
25404
25568
  const session = createCodingAgentSession(def.harness || cfg.harness, {
25405
25569
  cwd: dir,
25406
- model: def.model || cfg.model,
25570
+ model,
25407
25571
  resumeId,
25408
25572
  instructionsFile: INSTRUCTIONS_FILE,
25409
25573
  // undefined, not {}, when this agent has no tools — see the driver.
@@ -25490,7 +25654,7 @@ async function runCodingAgent(agentId, options = {}) {
25490
25654
  logger.warn(`[CodingAgent:${def.id}] deliverable rejected (${message}) \u2014 asking the agent to repair it`);
25491
25655
  const repair = createCodingAgentSession(def.harness || cfg.harness, {
25492
25656
  cwd: dir,
25493
- model: def.model || cfg.model,
25657
+ model,
25494
25658
  resumeId: result.sessionId,
25495
25659
  instructionsFile: INSTRUCTIONS_FILE,
25496
25660
  mcpServers: tools && Object.keys(tools).length ? tools : void 0,
@@ -30092,6 +30256,7 @@ export {
30092
30256
  bridgePreamble,
30093
30257
  captureContext,
30094
30258
  clearContextTokens,
30259
+ constraintPoliciesFromConfig,
30095
30260
  contextTokenCount,
30096
30261
  createCodexSession,
30097
30262
  createCodingAgentSession,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superatomai/sdk-node",
3
- "version": "0.0.87-dsp",
3
+ "version": "0.0.89-dsp",
4
4
  "description": "Node.js TypeScript SDK for Superatom",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -50,6 +50,7 @@
50
50
  "build": "tsc --noEmit && tsup",
51
51
  "dev": "tsup --watch",
52
52
  "test": "tsx test-sdk.ts",
53
+ "test:unit": "node --import tsx --test tests/unit/*.test.ts",
53
54
  "type-check": "tsc --noEmit"
54
55
  }
55
56
  }