dna-sdk 0.10.0 → 0.11.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/bootstrap.js +4 -0
- package/dist/extensions/cloud/kinds/tenant-plan.kind.yaml +105 -0
- package/dist/extensions/cloud/kinds/tier.kind.yaml +135 -0
- package/dist/extensions/cloud.d.ts +31 -0
- package/dist/extensions/cloud.js +39 -0
- package/dist/extensions/intel/kinds/insight.kind.yaml +148 -0
- package/dist/extensions/intel/kinds/intel-source.kind.yaml +117 -0
- package/dist/extensions/intel.d.ts +30 -0
- package/dist/extensions/intel.js +38 -0
- package/dist/extensions/sdlc/kinds/lesson-learned.kind.yaml +0 -1
- package/dist/kernel/index.d.ts +44 -0
- package/dist/kernel/index.js +85 -0
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -38,8 +38,10 @@ import { DocExtension } from "./extensions/doc.js";
|
|
|
38
38
|
import { ResearchExtension } from "./extensions/research.js";
|
|
39
39
|
import { TestkitExtension } from "./extensions/testkit.js";
|
|
40
40
|
import { ModelRegExtension } from "./extensions/modelreg.js";
|
|
41
|
+
import { CloudExtension } from "./extensions/cloud.js";
|
|
41
42
|
import { AutomationExtension } from "./extensions/automation.js";
|
|
42
43
|
import { EvalExtension } from "./extensions/eval.js";
|
|
44
|
+
import { IntelExtension } from "./extensions/intel.js";
|
|
43
45
|
import { loadConfig } from "./config.js";
|
|
44
46
|
import { resolveDefaultFsUrl, sourceFromUrl } from "./adapters/source-url.js";
|
|
45
47
|
/**
|
|
@@ -69,8 +71,10 @@ function loadBuiltins(target) {
|
|
|
69
71
|
new ResearchExtension(),
|
|
70
72
|
new TestkitExtension(),
|
|
71
73
|
new ModelRegExtension(),
|
|
74
|
+
new CloudExtension(),
|
|
72
75
|
new AutomationExtension(),
|
|
73
76
|
new EvalExtension(),
|
|
77
|
+
new IntelExtension(),
|
|
74
78
|
]) {
|
|
75
79
|
target.load(ext);
|
|
76
80
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# TenantPlan — the DNA Cloud tenant→Tier assignment (record plane).
|
|
2
|
+
# One doc per tenant maps it to its CURRENT Tier (free/pro/enterprise) as
|
|
3
|
+
# first-class GLOBAL data, so enforcement follows billing state without a
|
|
4
|
+
# redeploy. This is the billing→enforcement BRIDGE: dna-cloud's Stripe webhook
|
|
5
|
+
# writes this doc on subscribe/cancel, and the MCP server reads it via
|
|
6
|
+
# kernel.tenant_plan(tenant) when a token carries no explicit plan claim.
|
|
7
|
+
#
|
|
8
|
+
# CONTRACT — the OSS SDK only READS: zero Stripe/billing code lives here. The
|
|
9
|
+
# SDK resolves tier_id from THIS doc; dna-cloud (closed source) is the only
|
|
10
|
+
# writer. A missing TenantPlan is not an error — the MCP guard falls back to the
|
|
11
|
+
# Free floor.
|
|
12
|
+
#
|
|
13
|
+
# TenantPlans live in the `_lib` scope (`tenant-plans/<tenant>.yaml`) —
|
|
14
|
+
# TenantPlan is GLOBAL (base only, no per-tenant override) and NOT inheritable;
|
|
15
|
+
# kernel.tenant_plan() queries `_lib` directly regardless of the caller's scope.
|
|
16
|
+
#
|
|
17
|
+
# PARITY-CRITICAL package data: the byte-identical mirror lives at
|
|
18
|
+
# packages/sdk-{py,ts}/{dna/extensions,src/extensions}/cloud/kinds/
|
|
19
|
+
# tenant-plan.kind.yaml (tests/test_descriptor_hash_parity.py enforces).
|
|
20
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
21
|
+
kind: KindDefinition
|
|
22
|
+
metadata:
|
|
23
|
+
name: tenant-plan
|
|
24
|
+
spec:
|
|
25
|
+
target_api_version: github.com/ruinosus/dna/cloud/v1
|
|
26
|
+
target_kind: TenantPlan
|
|
27
|
+
alias: cloud-tenant-plan
|
|
28
|
+
origin: github.com/ruinosus/dna/cloud
|
|
29
|
+
tenant_scope: global
|
|
30
|
+
plane: record
|
|
31
|
+
prompt_target: false
|
|
32
|
+
flatten_in_context: false
|
|
33
|
+
prompt_target_priority: 0
|
|
34
|
+
storage:
|
|
35
|
+
type: yaml
|
|
36
|
+
container: tenant-plans
|
|
37
|
+
schema:
|
|
38
|
+
type: object
|
|
39
|
+
additionalProperties: false
|
|
40
|
+
required:
|
|
41
|
+
- tenant
|
|
42
|
+
- tier_id
|
|
43
|
+
properties:
|
|
44
|
+
tenant:
|
|
45
|
+
type: string
|
|
46
|
+
description: The DNA tenant this assignment is for. The doc name SHOULD
|
|
47
|
+
equal the tenant; kernel.tenant_plan() matches on this field.
|
|
48
|
+
tier_id:
|
|
49
|
+
type: string
|
|
50
|
+
description: The assigned Tier's id, e.g. free, pro, enterprise. Resolved
|
|
51
|
+
to caps via kernel.tier(tier_id) — never a literal in code.
|
|
52
|
+
source:
|
|
53
|
+
type: string
|
|
54
|
+
description: Where the assignment came from, e.g. stripe / manual / trial.
|
|
55
|
+
stripe_customer_id:
|
|
56
|
+
type: string
|
|
57
|
+
description: The Stripe customer id backing the assignment (dna-cloud
|
|
58
|
+
writes it; the OSS SDK never calls Stripe).
|
|
59
|
+
stripe_subscription_id:
|
|
60
|
+
type: string
|
|
61
|
+
description: The Stripe subscription id backing the assignment (dna-cloud
|
|
62
|
+
writes it; the OSS SDK never calls Stripe).
|
|
63
|
+
status:
|
|
64
|
+
type: string
|
|
65
|
+
description: The billing status of the assignment, e.g. active / past_due
|
|
66
|
+
/ canceled.
|
|
67
|
+
updated_at:
|
|
68
|
+
type: string
|
|
69
|
+
format: date-time
|
|
70
|
+
description: When dna-cloud last wrote this assignment (ISO 8601).
|
|
71
|
+
notes:
|
|
72
|
+
type:
|
|
73
|
+
- string
|
|
74
|
+
- 'null'
|
|
75
|
+
description: Free-form operator notes.
|
|
76
|
+
describe: '{tenant} → {tier_id}'
|
|
77
|
+
summary:
|
|
78
|
+
tenant: ''
|
|
79
|
+
tier_id: ''
|
|
80
|
+
status: ''
|
|
81
|
+
ui:
|
|
82
|
+
mode: govern
|
|
83
|
+
label:
|
|
84
|
+
en: Tenant Plans
|
|
85
|
+
pt-BR: Planos por Tenant
|
|
86
|
+
icon: 🎫
|
|
87
|
+
description:
|
|
88
|
+
en: DNA Cloud tenant→Tier assignments (billing→enforcement bridge).
|
|
89
|
+
pt-BR: Atribuições tenant→Tier do DNA Cloud (ponte cobrança→enforcement).
|
|
90
|
+
routes:
|
|
91
|
+
list: docs/TenantPlan
|
|
92
|
+
detail: docs/TenantPlan/:name
|
|
93
|
+
permissions:
|
|
94
|
+
list: any
|
|
95
|
+
detail: any
|
|
96
|
+
in_sidebar: true
|
|
97
|
+
display_order: 55
|
|
98
|
+
ascii_icon: 🎫
|
|
99
|
+
display_label: Tenant Plans
|
|
100
|
+
docs: A TenantPlan maps one DNA tenant to its current Tier as GLOBAL
|
|
101
|
+
declarative data, so enforcement follows billing state without a redeploy.
|
|
102
|
+
dna-cloud's Stripe webhook writes it on subscribe/cancel; the MCP server
|
|
103
|
+
reads it via kernel.tenant_plan(tenant) when the token carries no explicit
|
|
104
|
+
plan claim — zero Stripe/billing code lives in the OSS SDK, which only reads
|
|
105
|
+
the assignment.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Tier — DNA Cloud pricing-tier registry (record plane).
|
|
2
|
+
# One doc per plan (free/pro/enterprise): hard caps (calls_per_day, rate_per_sec,
|
|
3
|
+
# max_tenants) + which feature families it unlocks + price, as first-class GLOBAL
|
|
4
|
+
# data so a limit change is a file edit, never a redeploy. NOT named `Plan` — that
|
|
5
|
+
# alias belongs to the SDLC implementation-plan Kind; a pricing plan is a Tier.
|
|
6
|
+
#
|
|
7
|
+
# CONTRACT — never hardcode caps: the quota enforcer reads calls_per_day /
|
|
8
|
+
# rate_per_sec / max_tenants from THIS registry via kernel.tier(id_or_alias); a
|
|
9
|
+
# cap literal in code is a bug.
|
|
10
|
+
#
|
|
11
|
+
# Tiers live in the `_lib` scope (`tiers/<tier_id>.yaml`) — Tier is GLOBAL (base
|
|
12
|
+
# only, no per-tenant override) and NOT inheritable; kernel.tier() queries `_lib`
|
|
13
|
+
# directly regardless of the caller's scope.
|
|
14
|
+
#
|
|
15
|
+
# PARITY-CRITICAL package data: the byte-identical mirror lives at
|
|
16
|
+
# packages/sdk-{py,ts}/{dna/extensions,src/extensions}/cloud/kinds/
|
|
17
|
+
# tier.kind.yaml (tests/test_descriptor_hash_parity.py enforces).
|
|
18
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
19
|
+
kind: KindDefinition
|
|
20
|
+
metadata:
|
|
21
|
+
name: tier
|
|
22
|
+
spec:
|
|
23
|
+
target_api_version: github.com/ruinosus/dna/cloud/v1
|
|
24
|
+
target_kind: Tier
|
|
25
|
+
alias: cloud-tier
|
|
26
|
+
origin: github.com/ruinosus/dna/cloud
|
|
27
|
+
tenant_scope: global
|
|
28
|
+
plane: record
|
|
29
|
+
prompt_target: false
|
|
30
|
+
flatten_in_context: false
|
|
31
|
+
prompt_target_priority: 0
|
|
32
|
+
storage:
|
|
33
|
+
type: yaml
|
|
34
|
+
container: tiers
|
|
35
|
+
schema:
|
|
36
|
+
type: object
|
|
37
|
+
additionalProperties: false
|
|
38
|
+
required:
|
|
39
|
+
- tier_id
|
|
40
|
+
- display_name
|
|
41
|
+
properties:
|
|
42
|
+
tier_id:
|
|
43
|
+
type: string
|
|
44
|
+
description: Canonical tier id, e.g. free, pro, enterprise. The doc name
|
|
45
|
+
SHOULD equal the tier_id; kernel.tier() matches on this field first.
|
|
46
|
+
display_name:
|
|
47
|
+
type: string
|
|
48
|
+
description: Human-facing plan name, e.g. Free, Pro, Enterprise.
|
|
49
|
+
price_usd_month:
|
|
50
|
+
type: number
|
|
51
|
+
default: 0
|
|
52
|
+
description: Flat monthly price in USD (0 for the free tier).
|
|
53
|
+
calls_per_day:
|
|
54
|
+
type:
|
|
55
|
+
- integer
|
|
56
|
+
- 'null'
|
|
57
|
+
description: Daily call quota. Null = unlimited (enterprise). THE value
|
|
58
|
+
the quota enforcer reads — never hardcode it in code.
|
|
59
|
+
rate_per_sec:
|
|
60
|
+
type:
|
|
61
|
+
- integer
|
|
62
|
+
- 'null'
|
|
63
|
+
description: Per-second rate limit. Null = unmetered.
|
|
64
|
+
max_tenants:
|
|
65
|
+
type:
|
|
66
|
+
- integer
|
|
67
|
+
- 'null'
|
|
68
|
+
description: Number of tenants the plan allows. Null = unlimited.
|
|
69
|
+
feature_families:
|
|
70
|
+
type: array
|
|
71
|
+
items:
|
|
72
|
+
type: string
|
|
73
|
+
description: Tool families this tier unlocks, e.g.
|
|
74
|
+
[definitions, sdlc, memory, emit].
|
|
75
|
+
memory_mode:
|
|
76
|
+
type: string
|
|
77
|
+
enum:
|
|
78
|
+
- none
|
|
79
|
+
- read
|
|
80
|
+
- write
|
|
81
|
+
default: none
|
|
82
|
+
description: Memory access level granted by the tier — none, read, or
|
|
83
|
+
write.
|
|
84
|
+
overage_per_1k_usd:
|
|
85
|
+
type:
|
|
86
|
+
- number
|
|
87
|
+
- 'null'
|
|
88
|
+
description: USD charged per 1k calls above the daily quota. Null = no
|
|
89
|
+
overage (hard cap).
|
|
90
|
+
sla:
|
|
91
|
+
type: boolean
|
|
92
|
+
default: false
|
|
93
|
+
description: True when the tier includes a support/uptime SLA
|
|
94
|
+
(enterprise).
|
|
95
|
+
aliases:
|
|
96
|
+
type: array
|
|
97
|
+
items:
|
|
98
|
+
type: string
|
|
99
|
+
description: Alternate ids that resolve to this tier (legacy plan names).
|
|
100
|
+
kernel.tier() matches these on pass 2.
|
|
101
|
+
notes:
|
|
102
|
+
type:
|
|
103
|
+
- string
|
|
104
|
+
- 'null'
|
|
105
|
+
description: Free-form operator notes.
|
|
106
|
+
describe: '{display_name} (${price_usd_month}/mo)'
|
|
107
|
+
summary:
|
|
108
|
+
tier_id: ''
|
|
109
|
+
display_name: ''
|
|
110
|
+
price_usd_month: 0
|
|
111
|
+
calls_per_day: null
|
|
112
|
+
ui:
|
|
113
|
+
mode: govern
|
|
114
|
+
label:
|
|
115
|
+
en: Tiers
|
|
116
|
+
pt-BR: Planos
|
|
117
|
+
icon: 💳
|
|
118
|
+
description:
|
|
119
|
+
en: DNA Cloud pricing tiers (caps + feature families).
|
|
120
|
+
pt-BR: Planos do DNA Cloud (limites + famílias de features).
|
|
121
|
+
routes:
|
|
122
|
+
list: docs/Tier
|
|
123
|
+
detail: docs/Tier/:name
|
|
124
|
+
permissions:
|
|
125
|
+
list: any
|
|
126
|
+
detail: any
|
|
127
|
+
in_sidebar: true
|
|
128
|
+
display_order: 54
|
|
129
|
+
ascii_icon: 💳
|
|
130
|
+
display_label: Tiers
|
|
131
|
+
docs: A Tier declares one DNA Cloud plan's hard caps (calls/day, rate, tenants)
|
|
132
|
+
and which feature families it unlocks, as GLOBAL declarative data so changing
|
|
133
|
+
a limit is a file edit, not a redeploy. Resolve it via
|
|
134
|
+
kernel.tier(id_or_alias); the quota enforcer reads the caps from here and
|
|
135
|
+
never hardcodes them.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudExtension — DNA Cloud pricing + billing→enforcement registry.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python `dna.extensions.cloud`.
|
|
5
|
+
*
|
|
6
|
+
* Registers 2 Kinds, from descriptors (F3 — record Kinds are data, not
|
|
7
|
+
* classes):
|
|
8
|
+
*
|
|
9
|
+
* - Tier (`cloud-tier`) — one DNA Cloud plan's hard caps
|
|
10
|
+
* (`calls_per_day`, `rate_per_sec`, `max_tenants`) + the feature
|
|
11
|
+
* families it unlocks + price, as a first-class GLOBAL Kind so limits
|
|
12
|
+
* are project data, not implicit knowledge. NOT named `Plan` — that
|
|
13
|
+
* alias belongs to the SDLC implementation-plan Kind; a pricing plan is
|
|
14
|
+
* a Tier. Free / Pro / Enterprise are tiers.
|
|
15
|
+
* - TenantPlan (`cloud-tenant-plan`) — the tenant→Tier assignment: which
|
|
16
|
+
* Tier a given tenant is currently on. The billing→enforcement bridge:
|
|
17
|
+
* dna-cloud's Stripe webhook writes it on subscribe/cancel; the MCP
|
|
18
|
+
* server reads it via `kernel.tenantPlan(tenant)` when a token carries no
|
|
19
|
+
* explicit plan claim. The OSS SDK only READS — zero Stripe/billing code.
|
|
20
|
+
*
|
|
21
|
+
* CONTRACT — never hardcode caps. The single source of truth for a plan's
|
|
22
|
+
* limits is its Tier doc (`_lib` scope, `tiers/<tier_id>.yaml`), resolved
|
|
23
|
+
* via `kernel.tier(idOrAlias)`. The quota enforcer reads calls/day, rate
|
|
24
|
+
* and tenant caps from there — a cap literal in code is a bug.
|
|
25
|
+
*/
|
|
26
|
+
import type { ExtensionHost, Extension } from "../kernel/protocols.js";
|
|
27
|
+
export declare class CloudExtension implements Extension {
|
|
28
|
+
name: string;
|
|
29
|
+
version: string;
|
|
30
|
+
register(kernel: ExtensionHost): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CloudExtension — DNA Cloud pricing + billing→enforcement registry.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python `dna.extensions.cloud`.
|
|
5
|
+
*
|
|
6
|
+
* Registers 2 Kinds, from descriptors (F3 — record Kinds are data, not
|
|
7
|
+
* classes):
|
|
8
|
+
*
|
|
9
|
+
* - Tier (`cloud-tier`) — one DNA Cloud plan's hard caps
|
|
10
|
+
* (`calls_per_day`, `rate_per_sec`, `max_tenants`) + the feature
|
|
11
|
+
* families it unlocks + price, as a first-class GLOBAL Kind so limits
|
|
12
|
+
* are project data, not implicit knowledge. NOT named `Plan` — that
|
|
13
|
+
* alias belongs to the SDLC implementation-plan Kind; a pricing plan is
|
|
14
|
+
* a Tier. Free / Pro / Enterprise are tiers.
|
|
15
|
+
* - TenantPlan (`cloud-tenant-plan`) — the tenant→Tier assignment: which
|
|
16
|
+
* Tier a given tenant is currently on. The billing→enforcement bridge:
|
|
17
|
+
* dna-cloud's Stripe webhook writes it on subscribe/cancel; the MCP
|
|
18
|
+
* server reads it via `kernel.tenantPlan(tenant)` when a token carries no
|
|
19
|
+
* explicit plan claim. The OSS SDK only READS — zero Stripe/billing code.
|
|
20
|
+
*
|
|
21
|
+
* CONTRACT — never hardcode caps. The single source of truth for a plan's
|
|
22
|
+
* limits is its Tier doc (`_lib` scope, `tiers/<tier_id>.yaml`), resolved
|
|
23
|
+
* via `kernel.tier(idOrAlias)`. The quota enforcer reads calls/day, rate
|
|
24
|
+
* and tenant caps from there — a cap literal in code is a bug.
|
|
25
|
+
*/
|
|
26
|
+
import { loadDescriptors } from "../kernel/descriptor-loader.js";
|
|
27
|
+
export class CloudExtension {
|
|
28
|
+
name = "cloud";
|
|
29
|
+
version = "1.0.0";
|
|
30
|
+
register(kernel) {
|
|
31
|
+
// F3: Tier ships as kinds/tier.kind.yaml package data (byte-identical
|
|
32
|
+
// Py↔TS mirror), registered through the SAME funnel as per-scope
|
|
33
|
+
// KindDefinitions (plane lint + digest idempotency + builtin conflict
|
|
34
|
+
// marker).
|
|
35
|
+
for (const raw of loadDescriptors(import.meta.url, "cloud/kinds")) {
|
|
36
|
+
kernel.kindFromDescriptor(raw);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# IntelInsight — the dissemination unit (record plane; a ranked, actionable
|
|
2
|
+
# insight). Produced from an IntelSource; the ranker/digest/dedup/feedback stages
|
|
3
|
+
# all reference it. Named IntelInsight (NOT Insight) — the bare `Insight` name
|
|
4
|
+
# already belongs to the SDLC oracle Kind (alias sdlc-insight); two Kinds sharing
|
|
5
|
+
# a name makes every bare-name lookup ambiguous (i-195). The cross-stage contract
|
|
6
|
+
# is the alias `intel-insight`.
|
|
7
|
+
#
|
|
8
|
+
# TENANTED — per-tenant generated data (the tenant's own insight stream), NOT a
|
|
9
|
+
# shared `_lib` default. Deliberately NOT inheritable (never in
|
|
10
|
+
# DEFAULT_INHERITABLE_KINDS_V1); TENANTED is correct.
|
|
11
|
+
#
|
|
12
|
+
# Embeddable (embed: [title, fact]) so the later dedup story can do semantic
|
|
13
|
+
# recall over the insight text.
|
|
14
|
+
#
|
|
15
|
+
# PARITY-CRITICAL package data: the byte-identical mirror lives at
|
|
16
|
+
# packages/sdk-{py,ts}/{dna/extensions,src/extensions}/intel/kinds/
|
|
17
|
+
# insight.kind.yaml (tests/test_descriptor_hash_parity.py enforces).
|
|
18
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
19
|
+
kind: KindDefinition
|
|
20
|
+
metadata:
|
|
21
|
+
name: insight
|
|
22
|
+
spec:
|
|
23
|
+
target_api_version: github.com/ruinosus/dna/intel/v1
|
|
24
|
+
target_kind: IntelInsight
|
|
25
|
+
alias: intel-insight
|
|
26
|
+
origin: github.com/ruinosus/dna/intel
|
|
27
|
+
tenant_scope: tenanted
|
|
28
|
+
plane: record
|
|
29
|
+
prompt_target: false
|
|
30
|
+
flatten_in_context: false
|
|
31
|
+
prompt_target_priority: 0
|
|
32
|
+
storage:
|
|
33
|
+
type: yaml
|
|
34
|
+
container: intel-insights
|
|
35
|
+
schema:
|
|
36
|
+
type: object
|
|
37
|
+
additionalProperties: false
|
|
38
|
+
required:
|
|
39
|
+
- title
|
|
40
|
+
- fact
|
|
41
|
+
- score
|
|
42
|
+
- state
|
|
43
|
+
properties:
|
|
44
|
+
title:
|
|
45
|
+
type: string
|
|
46
|
+
description: The insight headline.
|
|
47
|
+
fact:
|
|
48
|
+
type: string
|
|
49
|
+
description: What happened / the cited fact.
|
|
50
|
+
why:
|
|
51
|
+
type:
|
|
52
|
+
- string
|
|
53
|
+
- 'null'
|
|
54
|
+
description: Why it matters to this source.
|
|
55
|
+
action:
|
|
56
|
+
type:
|
|
57
|
+
- string
|
|
58
|
+
- 'null'
|
|
59
|
+
description: The single suggested action.
|
|
60
|
+
score:
|
|
61
|
+
type: number
|
|
62
|
+
default: 0
|
|
63
|
+
description: Actionability score (0..1). The ranker sets this; the digest
|
|
64
|
+
suppresses insights scoring below the source's threshold.
|
|
65
|
+
source_ref:
|
|
66
|
+
type:
|
|
67
|
+
- string
|
|
68
|
+
- 'null'
|
|
69
|
+
description: The IntelSource name this insight came from.
|
|
70
|
+
pirs:
|
|
71
|
+
type: array
|
|
72
|
+
items:
|
|
73
|
+
type: string
|
|
74
|
+
description: Which Priority Intelligence Requirements this insight matches.
|
|
75
|
+
citations:
|
|
76
|
+
type: array
|
|
77
|
+
items:
|
|
78
|
+
type: object
|
|
79
|
+
additionalProperties: false
|
|
80
|
+
required:
|
|
81
|
+
- url
|
|
82
|
+
properties:
|
|
83
|
+
url:
|
|
84
|
+
type: string
|
|
85
|
+
title:
|
|
86
|
+
type:
|
|
87
|
+
- string
|
|
88
|
+
- 'null'
|
|
89
|
+
description: Sources backing the fact — each a {url, title} pair.
|
|
90
|
+
state:
|
|
91
|
+
type: string
|
|
92
|
+
enum:
|
|
93
|
+
- new
|
|
94
|
+
- actioned
|
|
95
|
+
- dismissed
|
|
96
|
+
- snoozed
|
|
97
|
+
default: new
|
|
98
|
+
description: The feedback disposition — the reader's response to the insight.
|
|
99
|
+
evidence_rating:
|
|
100
|
+
type: string
|
|
101
|
+
enum:
|
|
102
|
+
- evidence-based
|
|
103
|
+
- opinion-practice
|
|
104
|
+
- anecdotal
|
|
105
|
+
default: anecdotal
|
|
106
|
+
description: How well-grounded the fact is — evidence-based, opinion/practice,
|
|
107
|
+
or anecdotal.
|
|
108
|
+
created_at:
|
|
109
|
+
type:
|
|
110
|
+
- string
|
|
111
|
+
- 'null'
|
|
112
|
+
description: ISO-8601 timestamp, stamped by the writer (not defaulted here).
|
|
113
|
+
describe: '{title}'
|
|
114
|
+
summary:
|
|
115
|
+
title: ''
|
|
116
|
+
score: 0
|
|
117
|
+
state: ''
|
|
118
|
+
source_ref: null
|
|
119
|
+
embed:
|
|
120
|
+
- title
|
|
121
|
+
- fact
|
|
122
|
+
ui:
|
|
123
|
+
mode: govern
|
|
124
|
+
label:
|
|
125
|
+
en: Intel Insights
|
|
126
|
+
pt-BR: Insights de Inteligência
|
|
127
|
+
icon: 💡
|
|
128
|
+
description:
|
|
129
|
+
en: Ranked, actionable insights (the dissemination unit of the intel layer).
|
|
130
|
+
pt-BR: Insights acionáveis e ranqueados (a unidade de disseminação da camada
|
|
131
|
+
de inteligência).
|
|
132
|
+
routes:
|
|
133
|
+
list: docs/IntelInsight
|
|
134
|
+
detail: docs/IntelInsight/:name
|
|
135
|
+
permissions:
|
|
136
|
+
list: any
|
|
137
|
+
detail: any
|
|
138
|
+
in_sidebar: true
|
|
139
|
+
display_order: 57
|
|
140
|
+
ascii_icon: 💡
|
|
141
|
+
display_label: Intel Insights
|
|
142
|
+
docs: An IntelInsight is the dissemination unit of the intelligence layer — a
|
|
143
|
+
ranked, actionable insight produced from an IntelSource, carrying its headline,
|
|
144
|
+
cited fact, suggested action, actionability score, matched PIRs, citations,
|
|
145
|
+
evidence rating and feedback state. The ranker sets the score; the digest
|
|
146
|
+
suppresses insights below the source threshold; the feedback stage records the
|
|
147
|
+
state (new/actioned/dismissed/snoozed). Embeddable so a later dedup stage can
|
|
148
|
+
recall semantically similar insights.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# IntelSource — a watched portfolio source (record plane; the "Direction" stage).
|
|
2
|
+
# One doc per source (a repo, a scope, or an external URL) declares what the DNA
|
|
3
|
+
# observes: its research cadence, actionability threshold, Priority Intelligence
|
|
4
|
+
# Requirements (PIRs) and mute state.
|
|
5
|
+
#
|
|
6
|
+
# TENANTED — a source is the tenant's OWN watchlist (per-tenant user data), NOT a
|
|
7
|
+
# shared `_lib` default consumed by other scopes. It is deliberately NOT
|
|
8
|
+
# inheritable (never in DEFAULT_INHERITABLE_KINDS_V1); TENANTED is the correct
|
|
9
|
+
# tenancy for per-tenant data without a platform default (the maxim
|
|
10
|
+
# "inheritable ⇒ never TENANTED" does not apply — this Kind is not inheritable).
|
|
11
|
+
#
|
|
12
|
+
# PARITY-CRITICAL package data: the byte-identical mirror lives at
|
|
13
|
+
# packages/sdk-{py,ts}/{dna/extensions,src/extensions}/intel/kinds/
|
|
14
|
+
# intel-source.kind.yaml (tests/test_descriptor_hash_parity.py enforces).
|
|
15
|
+
apiVersion: github.com/ruinosus/dna/core/v1
|
|
16
|
+
kind: KindDefinition
|
|
17
|
+
metadata:
|
|
18
|
+
name: intel-source
|
|
19
|
+
spec:
|
|
20
|
+
target_api_version: github.com/ruinosus/dna/intel/v1
|
|
21
|
+
target_kind: IntelSource
|
|
22
|
+
alias: intel-source
|
|
23
|
+
origin: github.com/ruinosus/dna/intel
|
|
24
|
+
tenant_scope: tenanted
|
|
25
|
+
plane: record
|
|
26
|
+
prompt_target: false
|
|
27
|
+
flatten_in_context: false
|
|
28
|
+
prompt_target_priority: 0
|
|
29
|
+
storage:
|
|
30
|
+
type: yaml
|
|
31
|
+
container: intel-sources
|
|
32
|
+
schema:
|
|
33
|
+
type: object
|
|
34
|
+
additionalProperties: false
|
|
35
|
+
required:
|
|
36
|
+
- name
|
|
37
|
+
- type
|
|
38
|
+
properties:
|
|
39
|
+
name:
|
|
40
|
+
type: string
|
|
41
|
+
description: The source name, e.g. copiloto-medico. The doc name SHOULD
|
|
42
|
+
equal this.
|
|
43
|
+
type:
|
|
44
|
+
type: string
|
|
45
|
+
enum:
|
|
46
|
+
- repo
|
|
47
|
+
- scope
|
|
48
|
+
- external
|
|
49
|
+
description: What kind of source this is — a code repo, a DNA scope, or an
|
|
50
|
+
external URL/feed.
|
|
51
|
+
uri:
|
|
52
|
+
type:
|
|
53
|
+
- string
|
|
54
|
+
- 'null'
|
|
55
|
+
description: Path / URL / scope id the source points at. Null when the name
|
|
56
|
+
alone identifies it.
|
|
57
|
+
cadence:
|
|
58
|
+
type: string
|
|
59
|
+
enum:
|
|
60
|
+
- manual
|
|
61
|
+
- event
|
|
62
|
+
- daily
|
|
63
|
+
- weekly
|
|
64
|
+
default: weekly
|
|
65
|
+
description: How often the source is researched — manual (on demand), event
|
|
66
|
+
(on a trigger), daily, or weekly.
|
|
67
|
+
threshold:
|
|
68
|
+
type: number
|
|
69
|
+
default: 0.6
|
|
70
|
+
description: Actionability threshold (0..1) below which insights from this
|
|
71
|
+
source are suppressed. Insights scoring under it are not disseminated.
|
|
72
|
+
pirs:
|
|
73
|
+
type: array
|
|
74
|
+
items:
|
|
75
|
+
type: string
|
|
76
|
+
description: Priority Intelligence Requirements — focus areas that get
|
|
77
|
+
prioritized when researching this source.
|
|
78
|
+
muted:
|
|
79
|
+
type: boolean
|
|
80
|
+
default: false
|
|
81
|
+
description: True to pause research on this source without deleting it.
|
|
82
|
+
notes:
|
|
83
|
+
type:
|
|
84
|
+
- string
|
|
85
|
+
- 'null'
|
|
86
|
+
description: Free-form operator notes.
|
|
87
|
+
describe: '{name} ({type})'
|
|
88
|
+
summary:
|
|
89
|
+
name: ''
|
|
90
|
+
type: ''
|
|
91
|
+
cadence: ''
|
|
92
|
+
muted: false
|
|
93
|
+
ui:
|
|
94
|
+
mode: govern
|
|
95
|
+
label:
|
|
96
|
+
en: Intel Sources
|
|
97
|
+
pt-BR: Fontes de Inteligência
|
|
98
|
+
icon: 📡
|
|
99
|
+
description:
|
|
100
|
+
en: Watched portfolio sources (the Direction stage — what the DNA observes).
|
|
101
|
+
pt-BR: Fontes observadas do portfólio (estágio Direção — o que o DNA observa).
|
|
102
|
+
routes:
|
|
103
|
+
list: docs/IntelSource
|
|
104
|
+
detail: docs/IntelSource/:name
|
|
105
|
+
permissions:
|
|
106
|
+
list: any
|
|
107
|
+
detail: any
|
|
108
|
+
in_sidebar: true
|
|
109
|
+
display_order: 56
|
|
110
|
+
ascii_icon: 📡
|
|
111
|
+
display_label: Intel Sources
|
|
112
|
+
docs: An IntelSource declares one watched portfolio source (a repo, a scope, or an
|
|
113
|
+
external URL) the DNA observes — its research cadence, actionability threshold,
|
|
114
|
+
Priority Intelligence Requirements (PIRs) and mute state, as per-tenant
|
|
115
|
+
declarative data. It is the Direction stage of the intelligence layer — the
|
|
116
|
+
research → ranked insights → feedback pipeline reads active IntelSources and
|
|
117
|
+
researches each on its cadence.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntelExtension — DNA intelligence-layer data foundation.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python `dna.extensions.intel`.
|
|
5
|
+
*
|
|
6
|
+
* Registers 2 record Kinds, from descriptors (F3 — record Kinds are data, not
|
|
7
|
+
* classes):
|
|
8
|
+
*
|
|
9
|
+
* - IntelSource (`intel-source`) — a watched portfolio source (the
|
|
10
|
+
* "Direction" stage: what the DNA observes). One doc per source (a repo, a
|
|
11
|
+
* scope, or an external URL) carrying its research cadence, actionability
|
|
12
|
+
* threshold, Priority Intelligence Requirements (PIRs) and mute state.
|
|
13
|
+
* TENANTED — a source is the tenant's OWN watchlist (per-tenant user
|
|
14
|
+
* data), NOT a shared `_lib` default. It is deliberately NOT inheritable
|
|
15
|
+
* (never in DEFAULT_INHERITABLE_KINDS_V1), so TENANTED is correct.
|
|
16
|
+
* - Insight (`intel-insight`, kind name `IntelInsight`) — the
|
|
17
|
+
* dissemination unit: a ranked, actionable insight that the
|
|
18
|
+
* ranker/digest/dedup/feedback stages reference. TENANTED for the same
|
|
19
|
+
* reason (per-tenant generated data). Embeddable (`embed: [title, fact]`)
|
|
20
|
+
* so a later dedup story can do semantic recall.
|
|
21
|
+
*
|
|
22
|
+
* This is the foundation ONLY — the research/ranker/digest/feedback stages
|
|
23
|
+
* land in later stories; here we ship just the two data Kinds.
|
|
24
|
+
*/
|
|
25
|
+
import type { ExtensionHost, Extension } from "../kernel/protocols.js";
|
|
26
|
+
export declare class IntelExtension implements Extension {
|
|
27
|
+
name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
register(kernel: ExtensionHost): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IntelExtension — DNA intelligence-layer data foundation.
|
|
3
|
+
*
|
|
4
|
+
* 1:1 parity with Python `dna.extensions.intel`.
|
|
5
|
+
*
|
|
6
|
+
* Registers 2 record Kinds, from descriptors (F3 — record Kinds are data, not
|
|
7
|
+
* classes):
|
|
8
|
+
*
|
|
9
|
+
* - IntelSource (`intel-source`) — a watched portfolio source (the
|
|
10
|
+
* "Direction" stage: what the DNA observes). One doc per source (a repo, a
|
|
11
|
+
* scope, or an external URL) carrying its research cadence, actionability
|
|
12
|
+
* threshold, Priority Intelligence Requirements (PIRs) and mute state.
|
|
13
|
+
* TENANTED — a source is the tenant's OWN watchlist (per-tenant user
|
|
14
|
+
* data), NOT a shared `_lib` default. It is deliberately NOT inheritable
|
|
15
|
+
* (never in DEFAULT_INHERITABLE_KINDS_V1), so TENANTED is correct.
|
|
16
|
+
* - Insight (`intel-insight`, kind name `IntelInsight`) — the
|
|
17
|
+
* dissemination unit: a ranked, actionable insight that the
|
|
18
|
+
* ranker/digest/dedup/feedback stages reference. TENANTED for the same
|
|
19
|
+
* reason (per-tenant generated data). Embeddable (`embed: [title, fact]`)
|
|
20
|
+
* so a later dedup story can do semantic recall.
|
|
21
|
+
*
|
|
22
|
+
* This is the foundation ONLY — the research/ranker/digest/feedback stages
|
|
23
|
+
* land in later stories; here we ship just the two data Kinds.
|
|
24
|
+
*/
|
|
25
|
+
import { loadDescriptors } from "../kernel/descriptor-loader.js";
|
|
26
|
+
export class IntelExtension {
|
|
27
|
+
name = "intel";
|
|
28
|
+
version = "1.0.0";
|
|
29
|
+
register(kernel) {
|
|
30
|
+
// F3: both Kinds ship as kinds/*.kind.yaml package data (byte-identical
|
|
31
|
+
// Py↔TS mirror), registered through the SAME funnel as per-scope
|
|
32
|
+
// KindDefinitions (plane lint + digest idempotency + builtin conflict
|
|
33
|
+
// marker).
|
|
34
|
+
for (const raw of loadDescriptors(import.meta.url, "intel/kinds")) {
|
|
35
|
+
kernel.kindFromDescriptor(raw);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -55,6 +55,18 @@ export declare const MODEL_REGISTRY_SCOPE = "_lib";
|
|
|
55
55
|
* 1:1 parity with Python `Kernel._VOICE_POLICY_SCOPE`.
|
|
56
56
|
*/
|
|
57
57
|
export declare const VOICE_POLICY_SCOPE = "_lib";
|
|
58
|
+
/**
|
|
59
|
+
* The scope that owns the canonical Tier (DNA Cloud pricing) registry.
|
|
60
|
+
* GLOBAL like ModelProfile — queried directly regardless of the caller's
|
|
61
|
+
* scope. 1:1 parity with Python `RegistryAccessor._TIER_REGISTRY_SCOPE`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const TIER_REGISTRY_SCOPE = "_lib";
|
|
64
|
+
/**
|
|
65
|
+
* The scope that owns the canonical TenantPlan (tenant→Tier assignment)
|
|
66
|
+
* registry. GLOBAL like Tier — queried directly regardless of the caller's
|
|
67
|
+
* scope. 1:1 parity with Python `RegistryAccessor._TENANT_PLAN_REGISTRY_SCOPE`.
|
|
68
|
+
*/
|
|
69
|
+
export declare const TENANT_PLAN_REGISTRY_SCOPE = "_lib";
|
|
58
70
|
export declare class Kernel {
|
|
59
71
|
private static readonly _BOOTSTRAP_KINDS;
|
|
60
72
|
private static readonly _LEGACY_NON_INHERITABLE;
|
|
@@ -611,6 +623,38 @@ export declare class Kernel {
|
|
|
611
623
|
* @returns The matching Document, or null on miss or error.
|
|
612
624
|
*/
|
|
613
625
|
modelProfile(modelIdOrAlias: string): Promise<import("./document.js").Document | null>;
|
|
626
|
+
/**
|
|
627
|
+
* Resolve a Tier (DNA Cloud pricing plan) from the `_lib` scope by
|
|
628
|
+
* `tier_id` (pass 1) or `aliases[]` (pass 2). Returns the matching
|
|
629
|
+
* Document or null on miss.
|
|
630
|
+
*
|
|
631
|
+
* Always queries `TIER_REGISTRY_SCOPE` ("_lib") directly — Tier is GLOBAL
|
|
632
|
+
* and NOT in `INHERITABLE_KINDS`; any caller scope is irrelevant. The
|
|
633
|
+
* quota enforcer reads the caps from here — never hardcode calls_per_day /
|
|
634
|
+
* rate_per_sec / max_tenants in code.
|
|
635
|
+
*
|
|
636
|
+
* 1:1 parity with Python `Kernel.tier(tier_id_or_alias)`.
|
|
637
|
+
*
|
|
638
|
+
* @param idOrAlias - The tier_id string or an alias declared on the tier.
|
|
639
|
+
* @returns The matching Document, or null on miss or error.
|
|
640
|
+
*/
|
|
641
|
+
tier(idOrAlias: string): Promise<import("./document.js").Document | null>;
|
|
642
|
+
/**
|
|
643
|
+
* Resolve a TenantPlan (a tenant→Tier assignment) from the `_lib` scope by
|
|
644
|
+
* `spec.tenant`. Returns the matching Document or null when no assignment
|
|
645
|
+
* exists for `tenant`.
|
|
646
|
+
*
|
|
647
|
+
* Always queries `TENANT_PLAN_REGISTRY_SCOPE` ("_lib") directly — TenantPlan
|
|
648
|
+
* is GLOBAL and NOT in `INHERITABLE_KINDS`; any caller scope is irrelevant.
|
|
649
|
+
* The billing→enforcement bridge: dna-cloud's Stripe webhook writes the doc;
|
|
650
|
+
* the SDK only reads it here. No alias pass — the match is on `tenant`.
|
|
651
|
+
*
|
|
652
|
+
* 1:1 parity with Python `Kernel.tenant_plan(tenant)`.
|
|
653
|
+
*
|
|
654
|
+
* @param tenant - The DNA tenant to resolve the plan assignment for.
|
|
655
|
+
* @returns The matching Document, or null on miss or error.
|
|
656
|
+
*/
|
|
657
|
+
tenantPlan(tenant: string): Promise<import("./document.js").Document | null>;
|
|
614
658
|
/**
|
|
615
659
|
* Resolve a VoicePolicy from the `_lib` scope by metadata name.
|
|
616
660
|
* Returns the matching Document, the first policy as a fallback, or null
|
package/dist/kernel/index.js
CHANGED
|
@@ -59,6 +59,18 @@ export const MODEL_REGISTRY_SCOPE = SYSTEM_SCOPE;
|
|
|
59
59
|
* 1:1 parity with Python `Kernel._VOICE_POLICY_SCOPE`.
|
|
60
60
|
*/
|
|
61
61
|
export const VOICE_POLICY_SCOPE = SYSTEM_SCOPE;
|
|
62
|
+
/**
|
|
63
|
+
* The scope that owns the canonical Tier (DNA Cloud pricing) registry.
|
|
64
|
+
* GLOBAL like ModelProfile — queried directly regardless of the caller's
|
|
65
|
+
* scope. 1:1 parity with Python `RegistryAccessor._TIER_REGISTRY_SCOPE`.
|
|
66
|
+
*/
|
|
67
|
+
export const TIER_REGISTRY_SCOPE = SYSTEM_SCOPE;
|
|
68
|
+
/**
|
|
69
|
+
* The scope that owns the canonical TenantPlan (tenant→Tier assignment)
|
|
70
|
+
* registry. GLOBAL like Tier — queried directly regardless of the caller's
|
|
71
|
+
* scope. 1:1 parity with Python `RegistryAccessor._TENANT_PLAN_REGISTRY_SCOPE`.
|
|
72
|
+
*/
|
|
73
|
+
export const TENANT_PLAN_REGISTRY_SCOPE = SYSTEM_SCOPE;
|
|
62
74
|
export class Kernel {
|
|
63
75
|
// ── Kind classification — DERIVED from KindPort attributes ──────────────
|
|
64
76
|
// s-kernel-kindport-classification-attrs: the kernel no longer hardcodes
|
|
@@ -1610,6 +1622,79 @@ export class Kernel {
|
|
|
1610
1622
|
}
|
|
1611
1623
|
return null;
|
|
1612
1624
|
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Resolve a Tier (DNA Cloud pricing plan) from the `_lib` scope by
|
|
1627
|
+
* `tier_id` (pass 1) or `aliases[]` (pass 2). Returns the matching
|
|
1628
|
+
* Document or null on miss.
|
|
1629
|
+
*
|
|
1630
|
+
* Always queries `TIER_REGISTRY_SCOPE` ("_lib") directly — Tier is GLOBAL
|
|
1631
|
+
* and NOT in `INHERITABLE_KINDS`; any caller scope is irrelevant. The
|
|
1632
|
+
* quota enforcer reads the caps from here — never hardcode calls_per_day /
|
|
1633
|
+
* rate_per_sec / max_tenants in code.
|
|
1634
|
+
*
|
|
1635
|
+
* 1:1 parity with Python `Kernel.tier(tier_id_or_alias)`.
|
|
1636
|
+
*
|
|
1637
|
+
* @param idOrAlias - The tier_id string or an alias declared on the tier.
|
|
1638
|
+
* @returns The matching Document, or null on miss or error.
|
|
1639
|
+
*/
|
|
1640
|
+
async tier(idOrAlias) {
|
|
1641
|
+
let rows;
|
|
1642
|
+
try {
|
|
1643
|
+
const mi = await this.instance(TIER_REGISTRY_SCOPE);
|
|
1644
|
+
rows = mi._all("Tier");
|
|
1645
|
+
}
|
|
1646
|
+
catch {
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
// Pass 1: exact tier_id match
|
|
1650
|
+
for (const row of rows) {
|
|
1651
|
+
const spec = (row.spec ?? {});
|
|
1652
|
+
if (spec.tier_id === idOrAlias) {
|
|
1653
|
+
return row;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
// Pass 2: aliases[]
|
|
1657
|
+
for (const row of rows) {
|
|
1658
|
+
const spec = (row.spec ?? {});
|
|
1659
|
+
const aliases = (spec.aliases ?? []);
|
|
1660
|
+
if (aliases.includes(idOrAlias)) {
|
|
1661
|
+
return row;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return null;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Resolve a TenantPlan (a tenant→Tier assignment) from the `_lib` scope by
|
|
1668
|
+
* `spec.tenant`. Returns the matching Document or null when no assignment
|
|
1669
|
+
* exists for `tenant`.
|
|
1670
|
+
*
|
|
1671
|
+
* Always queries `TENANT_PLAN_REGISTRY_SCOPE` ("_lib") directly — TenantPlan
|
|
1672
|
+
* is GLOBAL and NOT in `INHERITABLE_KINDS`; any caller scope is irrelevant.
|
|
1673
|
+
* The billing→enforcement bridge: dna-cloud's Stripe webhook writes the doc;
|
|
1674
|
+
* the SDK only reads it here. No alias pass — the match is on `tenant`.
|
|
1675
|
+
*
|
|
1676
|
+
* 1:1 parity with Python `Kernel.tenant_plan(tenant)`.
|
|
1677
|
+
*
|
|
1678
|
+
* @param tenant - The DNA tenant to resolve the plan assignment for.
|
|
1679
|
+
* @returns The matching Document, or null on miss or error.
|
|
1680
|
+
*/
|
|
1681
|
+
async tenantPlan(tenant) {
|
|
1682
|
+
let rows;
|
|
1683
|
+
try {
|
|
1684
|
+
const mi = await this.instance(TENANT_PLAN_REGISTRY_SCOPE);
|
|
1685
|
+
rows = mi._all("TenantPlan");
|
|
1686
|
+
}
|
|
1687
|
+
catch {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
for (const row of rows) {
|
|
1691
|
+
const spec = (row.spec ?? {});
|
|
1692
|
+
if (spec.tenant === tenant) {
|
|
1693
|
+
return row;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1613
1698
|
/**
|
|
1614
1699
|
* Resolve a VoicePolicy from the `_lib` scope by metadata name.
|
|
1615
1700
|
* Returns the matching Document, the first policy as a fallback, or null
|