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 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
+ }
@@ -25,7 +25,6 @@ spec:
25
25
  target_kind: LessonLearned
26
26
  alias: sdlc-lesson-learned
27
27
  origin: github.com/ruinosus/dna/sdlc
28
- tenant_scope: global
29
28
  plane: record
30
29
  prompt_target: false
31
30
  flatten_in_context: false
@@ -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
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dna-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "DNA — Domain Notation of Anything (TypeScript SDK)",
5
5
  "type": "module",
6
6
  "license": "MIT",