@ai-sdlc/orchestrator 0.10.0 → 0.13.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.
Files changed (156) hide show
  1. package/dist/adapters.d.ts +42 -3
  2. package/dist/adapters.js +133 -3
  3. package/dist/admission-composite.d.ts +112 -1
  4. package/dist/admission-composite.js +85 -4
  5. package/dist/admission-enrichment.d.ts +12 -3
  6. package/dist/admission-enrichment.js +64 -13
  7. package/dist/admission-score.d.ts +30 -0
  8. package/dist/admission-score.js +4 -1
  9. package/dist/backlog-adapter.d.ts +45 -0
  10. package/dist/backlog-adapter.js +145 -1
  11. package/dist/calibration.d.ts +81 -0
  12. package/dist/calibration.js +76 -0
  13. package/dist/cli/commands/init-features.d.ts +301 -2
  14. package/dist/cli/commands/init-features.js +634 -7
  15. package/dist/cli/commands/init-templates.d.ts +198 -1
  16. package/dist/cli/commands/init-templates.js +943 -1
  17. package/dist/cli/commands/init.d.ts +45 -0
  18. package/dist/cli/commands/init.js +147 -5
  19. package/dist/cli/commands/run.js +9 -1
  20. package/dist/cli/index.d.ts +11 -0
  21. package/dist/cli/index.js +42 -8
  22. package/dist/compliance/composer.d.ts +79 -0
  23. package/dist/compliance/composer.js +258 -0
  24. package/dist/compliance/errors.d.ts +64 -0
  25. package/dist/compliance/errors.js +85 -0
  26. package/dist/compliance/loader.d.ts +52 -0
  27. package/dist/compliance/loader.js +124 -0
  28. package/dist/compliance/types.d.ts +184 -0
  29. package/dist/compliance/types.js +41 -0
  30. package/dist/compliance-clearance.d.ts +269 -0
  31. package/dist/compliance-clearance.js +269 -0
  32. package/dist/config.js +17 -0
  33. package/dist/cost-tracker.d.ts +22 -0
  34. package/dist/cost-tracker.js +41 -0
  35. package/dist/database/adapters/external.js +5 -1
  36. package/dist/embedding/adapters/openai-text-embedding-3-small.d.ts +71 -0
  37. package/dist/embedding/adapters/openai-text-embedding-3-small.js +190 -0
  38. package/dist/embedding/consumers/tessellation-drift.d.ts +74 -0
  39. package/dist/embedding/consumers/tessellation-drift.js +76 -0
  40. package/dist/embedding/cross-provider.d.ts +78 -0
  41. package/dist/embedding/cross-provider.js +75 -0
  42. package/dist/embedding/deprecation.d.ts +151 -0
  43. package/dist/embedding/deprecation.js +229 -0
  44. package/dist/embedding/errors.d.ts +90 -0
  45. package/dist/embedding/errors.js +150 -0
  46. package/dist/embedding/index.d.ts +29 -0
  47. package/dist/embedding/index.js +24 -0
  48. package/dist/embedding/pipeline-load.d.ts +146 -0
  49. package/dist/embedding/pipeline-load.js +178 -0
  50. package/dist/embedding/registry.d.ts +45 -0
  51. package/dist/embedding/registry.js +61 -0
  52. package/dist/embedding/stale-vector.d.ts +110 -0
  53. package/dist/embedding/stale-vector.js +92 -0
  54. package/dist/embedding/storage/index.d.ts +51 -0
  55. package/dist/embedding/storage/index.js +43 -0
  56. package/dist/embedding/storage/jsonl-backend.d.ts +150 -0
  57. package/dist/embedding/storage/jsonl-backend.js +332 -0
  58. package/dist/embedding/storage/types.d.ts +135 -0
  59. package/dist/embedding/storage/types.js +13 -0
  60. package/dist/embedding/types.d.ts +180 -0
  61. package/dist/embedding/types.js +10 -0
  62. package/dist/execute.d.ts +29 -2
  63. package/dist/execute.js +171 -30
  64. package/dist/index.d.ts +14 -2
  65. package/dist/index.js +19 -2
  66. package/dist/journey/inheritance-validator.d.ts +396 -0
  67. package/dist/journey/inheritance-validator.js +370 -0
  68. package/dist/journey/state-id-drift-rule.d.ts +137 -0
  69. package/dist/journey/state-id-drift-rule.js +245 -0
  70. package/dist/journey-sa2-router.d.ts +395 -0
  71. package/dist/journey-sa2-router.js +308 -0
  72. package/dist/runners/review-agent.js +6 -2
  73. package/dist/runners/runner-registry.d.ts +36 -0
  74. package/dist/runners/runner-registry.js +90 -0
  75. package/dist/runtime/attestations.d.ts +173 -13
  76. package/dist/runtime/attestations.js +252 -40
  77. package/dist/runtime/index.d.ts +1 -1
  78. package/dist/runtime/index.js +1 -1
  79. package/dist/sa-scoring/layer3-llm.js +6 -1
  80. package/dist/sa-scoring/revision-proposal-config.d.ts +178 -0
  81. package/dist/sa-scoring/revision-proposal-config.js +198 -0
  82. package/dist/sa-scoring/revision-proposal.d.ts +285 -0
  83. package/dist/sa-scoring/revision-proposal.js +417 -0
  84. package/dist/signal-ingestion/adapters/community-thread.d.ts +43 -0
  85. package/dist/signal-ingestion/adapters/community-thread.js +55 -0
  86. package/dist/signal-ingestion/adapters/in-app-feedback.d.ts +67 -0
  87. package/dist/signal-ingestion/adapters/in-app-feedback.js +51 -0
  88. package/dist/signal-ingestion/adapters/manual.d.ts +78 -0
  89. package/dist/signal-ingestion/adapters/manual.js +112 -0
  90. package/dist/signal-ingestion/adapters/support-ticket.d.ts +47 -0
  91. package/dist/signal-ingestion/adapters/support-ticket.js +51 -0
  92. package/dist/signal-ingestion/classifier.d.ts +205 -0
  93. package/dist/signal-ingestion/classifier.js +494 -0
  94. package/dist/signal-ingestion/clustering-types.d.ts +36 -0
  95. package/dist/signal-ingestion/clustering-types.js +14 -0
  96. package/dist/signal-ingestion/clustering.d.ts +200 -0
  97. package/dist/signal-ingestion/clustering.js +413 -0
  98. package/dist/signal-ingestion/config.d.ts +351 -0
  99. package/dist/signal-ingestion/config.js +587 -0
  100. package/dist/signal-ingestion/d1.d.ts +252 -0
  101. package/dist/signal-ingestion/d1.js +235 -0
  102. package/dist/signal-ingestion/errors.d.ts +73 -0
  103. package/dist/signal-ingestion/errors.js +108 -0
  104. package/dist/signal-ingestion/governance-events.d.ts +181 -0
  105. package/dist/signal-ingestion/governance-events.js +189 -0
  106. package/dist/signal-ingestion/index.d.ts +35 -0
  107. package/dist/signal-ingestion/index.js +53 -0
  108. package/dist/signal-ingestion/manual-share-metric.d.ts +93 -0
  109. package/dist/signal-ingestion/manual-share-metric.js +106 -0
  110. package/dist/signal-ingestion/registry.d.ts +40 -0
  111. package/dist/signal-ingestion/registry.js +137 -0
  112. package/dist/signal-ingestion/residency.d.ts +227 -0
  113. package/dist/signal-ingestion/residency.js +238 -0
  114. package/dist/signal-ingestion/significance.d.ts +554 -0
  115. package/dist/signal-ingestion/significance.js +555 -0
  116. package/dist/signal-ingestion/types.d.ts +191 -0
  117. package/dist/signal-ingestion/types.js +8 -0
  118. package/dist/substrate/drift-composition.d.ts +270 -0
  119. package/dist/substrate/drift-composition.js +306 -0
  120. package/dist/substrate/drift-tui-surface.d.ts +61 -0
  121. package/dist/substrate/drift-tui-surface.js +102 -0
  122. package/dist/substrate/identity-class.d.ts +176 -0
  123. package/dist/substrate/identity-class.js +201 -0
  124. package/dist/tessellation/cross-soul-provenance-rule.d.ts +133 -0
  125. package/dist/tessellation/cross-soul-provenance-rule.js +171 -0
  126. package/dist/tessellation/inter-soul-embedding-distance-rule.d.ts +61 -0
  127. package/dist/tessellation/inter-soul-embedding-distance-rule.js +67 -0
  128. package/dist/tessellation/rule-registry.d.ts +269 -0
  129. package/dist/tessellation/rule-registry.js +92 -0
  130. package/dist/tessellation/soul-slug-ast-scan-rule.d.ts +90 -0
  131. package/dist/tessellation/soul-slug-ast-scan-rule.js +158 -0
  132. package/dist/tessellation-admission.d.ts +162 -0
  133. package/dist/tessellation-admission.js +146 -0
  134. package/dist/tessellation-drift.d.ts +246 -0
  135. package/dist/tessellation-drift.js +250 -0
  136. package/dist/validate-config.js +13 -0
  137. package/dist/validate-issue.js +2 -2
  138. package/dist/variant/cardinality-activation.d.ts +126 -0
  139. package/dist/variant/cardinality-activation.js +101 -0
  140. package/dist/variant/deprecation-lifecycle.d.ts +184 -0
  141. package/dist/variant/deprecation-lifecycle.js +208 -0
  142. package/dist/variant/drift-extension.d.ts +136 -0
  143. package/dist/variant/drift-extension.js +164 -0
  144. package/dist/variant/engineering-review.d.ts +185 -0
  145. package/dist/variant/engineering-review.js +142 -0
  146. package/dist/variant/index.d.ts +32 -0
  147. package/dist/variant/index.js +32 -0
  148. package/dist/variant/inheritance-validator.d.ts +165 -0
  149. package/dist/variant/inheritance-validator.js +139 -0
  150. package/dist/variant/internal-adopter/index.d.ts +11 -0
  151. package/dist/variant/internal-adopter/index.js +10 -0
  152. package/dist/variant/internal-adopter/products.d.ts +156 -0
  153. package/dist/variant/internal-adopter/products.js +366 -0
  154. package/dist/variant-admission.d.ts +316 -0
  155. package/dist/variant-admission.js +247 -0
  156. package/package.json +10 -8
@@ -0,0 +1,78 @@
1
+ import type { RawSignal, SignalSourceAdapter } from '../types.js';
2
+ /**
3
+ * RFC-0030 OQ-13.4 v0.3 re-walkthrough default cap. 10 manual signals per
4
+ * operator per UTC day. Reasoning: a single operator processing >10
5
+ * high-signal conversations per day for the framework specifically is
6
+ * exceptional; 10/day allows ~50/week (sales-team conference-week scenarios);
7
+ * per-org override via `manualEntry.dailyCapPerOperator` for genuinely
8
+ * high-throughput contexts. Anti-gaming guardrail: makes sustained gaming
9
+ * require sustained effort while not blocking legitimate use.
10
+ */
11
+ export declare const DEFAULT_MANUAL_DAILY_CAP_PER_OPERATOR = 10;
12
+ export type ManualSignalInput = Omit<RawSignal, 'attestedAt'> & {
13
+ attestedAt?: Date;
14
+ };
15
+ export interface ManualSignalSourceAdapterOptions {
16
+ /**
17
+ * Per-operator daily cap (UTC day buckets). When exceeded, `addSignal`
18
+ * raises `ManualSignalRateLimitExceeded` so the registry can surface
19
+ * `Decision: manual-signal-rate-limit-exceeded`. Defaults to
20
+ * `DEFAULT_MANUAL_DAILY_CAP_PER_OPERATOR` (10). Setting to `0` or a negative
21
+ * value DISABLES rate limiting (operator opt-out).
22
+ */
23
+ dailyCapPerOperator?: number;
24
+ /**
25
+ * Seed entries pre-populating the adapter. Useful for tests; production
26
+ * adapters typically construct empty then `addSignal()` per CLI submit.
27
+ */
28
+ initialSignals?: ManualSignalInput[];
29
+ }
30
+ export declare class ManualSignalSourceAdapter implements SignalSourceAdapter {
31
+ readonly name = "signal-source-manual";
32
+ readonly defaultTier = 1;
33
+ /** Manual entry never requires OAuth (no upstream service). */
34
+ readonly requiresOAuth = false;
35
+ private readonly signals;
36
+ private readonly dailyCapPerOperator;
37
+ /**
38
+ * Per-operator UTC-day bucket counter:
39
+ * key = `${attestedBy}|${YYYY-MM-DD}` (UTC date)
40
+ * val = count of manual signals accepted in that bucket
41
+ *
42
+ * Rebuilt lazily — addSignal increments + reads from this map; the bucket
43
+ * key derivation uses the signal's `attestedAt` (auto-filled to `now` when
44
+ * omitted, so the count always lands in the correct UTC day).
45
+ */
46
+ private readonly perOperatorPerDayCount;
47
+ constructor(initialSignalsOrOptions?: ManualSignalInput[] | ManualSignalSourceAdapterOptions);
48
+ /** Effective per-operator daily cap (post-defaulting). Test helper. */
49
+ get effectiveDailyCap(): number;
50
+ isAvailable(): Promise<boolean>;
51
+ /**
52
+ * Add a manual signal with attestation + rate-limit enforcement.
53
+ *
54
+ * Order of validation:
55
+ * 1. `attestedBy` required → `ManualSignalIncomplete` (preserved from v0.2).
56
+ * 2. `attestedAt` defaulted to `now`.
57
+ * 3. Per-operator UTC-day cap → `ManualSignalRateLimitExceeded` when
58
+ * `count(attestedBy, utcDate(attestedAt)) >= dailyCapPerOperator`.
59
+ * (Skipped when `dailyCapPerOperator <= 0`.)
60
+ *
61
+ * Returns the persisted RawSignal (with `attestedAt` filled + `evidenceUrl`
62
+ * preserved verbatim) when accepted.
63
+ */
64
+ addSignal(input: ManualSignalInput, now?: Date): RawSignal;
65
+ fetchSignals(since: Date): Promise<RawSignal[]>;
66
+ /**
67
+ * Test helper: current per-operator UTC-day count for the given attestedBy.
68
+ * Exposed so unit tests can assert rate-limit bookkeeping without reaching
69
+ * into the private map.
70
+ */
71
+ countForOperatorOnDate(attestedBy: string, utcDateIso: string): number;
72
+ }
73
+ /**
74
+ * UTC date key in `YYYY-MM-DD` form. Hoisted so the manual-share metric
75
+ * helper (see `manual-share-metric.ts`) can use the same bucketing rule.
76
+ */
77
+ export declare function utcDateKey(d: Date): string;
78
+ //# sourceMappingURL=manual.d.ts.map
@@ -0,0 +1,112 @@
1
+ import { ManualSignalIncomplete, ManualSignalRateLimitExceeded } from '../errors.js';
2
+ /**
3
+ * RFC-0030 OQ-13.4 v0.3 re-walkthrough default cap. 10 manual signals per
4
+ * operator per UTC day. Reasoning: a single operator processing >10
5
+ * high-signal conversations per day for the framework specifically is
6
+ * exceptional; 10/day allows ~50/week (sales-team conference-week scenarios);
7
+ * per-org override via `manualEntry.dailyCapPerOperator` for genuinely
8
+ * high-throughput contexts. Anti-gaming guardrail: makes sustained gaming
9
+ * require sustained effort while not blocking legitimate use.
10
+ */
11
+ export const DEFAULT_MANUAL_DAILY_CAP_PER_OPERATOR = 10;
12
+ export class ManualSignalSourceAdapter {
13
+ name = 'signal-source-manual';
14
+ defaultTier = 1;
15
+ /** Manual entry never requires OAuth (no upstream service). */
16
+ requiresOAuth = false;
17
+ signals = [];
18
+ dailyCapPerOperator;
19
+ /**
20
+ * Per-operator UTC-day bucket counter:
21
+ * key = `${attestedBy}|${YYYY-MM-DD}` (UTC date)
22
+ * val = count of manual signals accepted in that bucket
23
+ *
24
+ * Rebuilt lazily — addSignal increments + reads from this map; the bucket
25
+ * key derivation uses the signal's `attestedAt` (auto-filled to `now` when
26
+ * omitted, so the count always lands in the correct UTC day).
27
+ */
28
+ perOperatorPerDayCount = new Map();
29
+ constructor(initialSignalsOrOptions = []) {
30
+ let initial;
31
+ if (Array.isArray(initialSignalsOrOptions)) {
32
+ // Legacy ctor signature: `new ManualSignalSourceAdapter([...])`
33
+ initial = initialSignalsOrOptions;
34
+ this.dailyCapPerOperator = DEFAULT_MANUAL_DAILY_CAP_PER_OPERATOR;
35
+ }
36
+ else {
37
+ initial = initialSignalsOrOptions.initialSignals ?? [];
38
+ this.dailyCapPerOperator =
39
+ initialSignalsOrOptions.dailyCapPerOperator ?? DEFAULT_MANUAL_DAILY_CAP_PER_OPERATOR;
40
+ }
41
+ for (const signal of initial)
42
+ this.addSignal(signal);
43
+ }
44
+ /** Effective per-operator daily cap (post-defaulting). Test helper. */
45
+ get effectiveDailyCap() {
46
+ return this.dailyCapPerOperator;
47
+ }
48
+ async isAvailable() {
49
+ return true;
50
+ }
51
+ /**
52
+ * Add a manual signal with attestation + rate-limit enforcement.
53
+ *
54
+ * Order of validation:
55
+ * 1. `attestedBy` required → `ManualSignalIncomplete` (preserved from v0.2).
56
+ * 2. `attestedAt` defaulted to `now`.
57
+ * 3. Per-operator UTC-day cap → `ManualSignalRateLimitExceeded` when
58
+ * `count(attestedBy, utcDate(attestedAt)) >= dailyCapPerOperator`.
59
+ * (Skipped when `dailyCapPerOperator <= 0`.)
60
+ *
61
+ * Returns the persisted RawSignal (with `attestedAt` filled + `evidenceUrl`
62
+ * preserved verbatim) when accepted.
63
+ */
64
+ addSignal(input, now = new Date()) {
65
+ if (!input.attestedBy) {
66
+ throw new ManualSignalIncomplete(input.sourceId);
67
+ }
68
+ const attestedAt = input.attestedAt ?? now;
69
+ // Rate-limit bucket keys MUST use wall-clock `now`, not caller-supplied
70
+ // `attestedAt`. A spoofed attestedAt (e.g. `new Date('2099-01-01')`)
71
+ // would otherwise create a fresh per-day bucket on each submit and
72
+ // bypass the daily cap entirely. The persisted attestedAt remains
73
+ // caller-supplied for audit fidelity (OQ-13.4 v0.3 anti-gaming fix).
74
+ const utcDate = utcDateKey(now);
75
+ const bucketKey = `${input.attestedBy}|${utcDate}`;
76
+ if (this.dailyCapPerOperator > 0) {
77
+ const currentCount = this.perOperatorPerDayCount.get(bucketKey) ?? 0;
78
+ if (currentCount >= this.dailyCapPerOperator) {
79
+ throw new ManualSignalRateLimitExceeded(input.attestedBy, this.dailyCapPerOperator, utcDate, input.sourceId);
80
+ }
81
+ this.perOperatorPerDayCount.set(bucketKey, currentCount + 1);
82
+ }
83
+ const signal = {
84
+ ...input,
85
+ attestedAt,
86
+ };
87
+ this.signals.push(signal);
88
+ return signal;
89
+ }
90
+ async fetchSignals(since) {
91
+ return this.signals.filter((signal) => signal.sourceTimestamp >= since);
92
+ }
93
+ /**
94
+ * Test helper: current per-operator UTC-day count for the given attestedBy.
95
+ * Exposed so unit tests can assert rate-limit bookkeeping without reaching
96
+ * into the private map.
97
+ */
98
+ countForOperatorOnDate(attestedBy, utcDateIso) {
99
+ return this.perOperatorPerDayCount.get(`${attestedBy}|${utcDateIso}`) ?? 0;
100
+ }
101
+ }
102
+ /**
103
+ * UTC date key in `YYYY-MM-DD` form. Hoisted so the manual-share metric
104
+ * helper (see `manual-share-metric.ts`) can use the same bucketing rule.
105
+ */
106
+ export function utcDateKey(d) {
107
+ const y = d.getUTCFullYear();
108
+ const m = String(d.getUTCMonth() + 1).padStart(2, '0');
109
+ const day = String(d.getUTCDate()).padStart(2, '0');
110
+ return `${y}-${m}-${day}`;
111
+ }
112
+ //# sourceMappingURL=manual.js.map
@@ -0,0 +1,47 @@
1
+ import type { RawSignal, SignalSourceAdapter } from '../types.js';
2
+ /**
3
+ * Default env var holding the Zendesk personal access token (PAT) per
4
+ * RFC-0030 OQ-13.1 v0.3 re-walkthrough. Env-var-based credential model;
5
+ * OAuth-scoped Zendesk integration deferred to the credential-management RFC.
6
+ */
7
+ export declare const DEFAULT_SUPPORT_TICKET_ENV_VAR = "SIGNAL_ZENDESK_PAT";
8
+ export interface SupportTicketAdapterOptions {
9
+ signals?: RawSignal[];
10
+ available?: boolean;
11
+ /**
12
+ * Legacy test override (pre-OQ-13.1 v0.3): emits the collapsed
13
+ * `adapter-credential-invalid` Decision. Preserved for backward-compat.
14
+ * New tests should use `credentialNotConfigured` or `credentialRejected`.
15
+ */
16
+ credentialInvalid?: boolean;
17
+ /** RFC-0030 OQ-13.1 v0.3: env-var missing test override. */
18
+ credentialNotConfigured?: boolean;
19
+ /** RFC-0030 OQ-13.1 v0.3: env-var present but upstream rejected test override. */
20
+ credentialRejected?: boolean;
21
+ /** Override the env var name (defaults to `SIGNAL_ZENDESK_PAT`). */
22
+ envVarName?: string;
23
+ /** Optional explicit env accessor (test seam). Defaults to `process.env`. */
24
+ env?: NodeJS.ProcessEnv;
25
+ /**
26
+ * When `true`, the adapter probes its env var as part of availability /
27
+ * fetchSignals. Defaults to `false` so the legacy in-memory test usage
28
+ * (passing `signals: [...]` without env-var setup) continues to work.
29
+ *
30
+ * v1-shipping deployments should set `probeEnvVar: true` to opt in to the
31
+ * OQ-13.1 v0.3 dual-Decision routing.
32
+ */
33
+ probeEnvVar?: boolean;
34
+ }
35
+ export declare class SupportTicketSignalSourceAdapter implements SignalSourceAdapter {
36
+ private readonly options;
37
+ readonly name = "signal-source-support-ticket";
38
+ readonly defaultTier = 1;
39
+ /** OAuth NOT required for the v1 Zendesk PAT credential model. */
40
+ readonly requiresOAuth = false;
41
+ constructor(options?: SupportTicketAdapterOptions);
42
+ /** Resolve the env var name for this adapter. */
43
+ get envVarName(): string;
44
+ isAvailable(): Promise<boolean>;
45
+ fetchSignals(since: Date): Promise<RawSignal[]>;
46
+ }
47
+ //# sourceMappingURL=support-ticket.d.ts.map
@@ -0,0 +1,51 @@
1
+ import { AdapterCredentialInvalid, AdapterCredentialNotConfigured, AdapterCredentialRejected, } from '../errors.js';
2
+ /**
3
+ * Default env var holding the Zendesk personal access token (PAT) per
4
+ * RFC-0030 OQ-13.1 v0.3 re-walkthrough. Env-var-based credential model;
5
+ * OAuth-scoped Zendesk integration deferred to the credential-management RFC.
6
+ */
7
+ export const DEFAULT_SUPPORT_TICKET_ENV_VAR = 'SIGNAL_ZENDESK_PAT';
8
+ export class SupportTicketSignalSourceAdapter {
9
+ options;
10
+ name = 'signal-source-support-ticket';
11
+ defaultTier = 1;
12
+ /** OAuth NOT required for the v1 Zendesk PAT credential model. */
13
+ requiresOAuth = false;
14
+ constructor(options = {}) {
15
+ this.options = options;
16
+ }
17
+ /** Resolve the env var name for this adapter. */
18
+ get envVarName() {
19
+ return this.options.envVarName ?? DEFAULT_SUPPORT_TICKET_ENV_VAR;
20
+ }
21
+ async isAvailable() {
22
+ if (this.options.available !== undefined)
23
+ return this.options.available;
24
+ if (this.options.probeEnvVar) {
25
+ const env = this.options.env ?? process.env;
26
+ const v = env[this.envVarName];
27
+ return typeof v === 'string' && v.trim().length > 0;
28
+ }
29
+ return true;
30
+ }
31
+ async fetchSignals(since) {
32
+ if (this.options.credentialNotConfigured) {
33
+ throw new AdapterCredentialNotConfigured(this.name, this.envVarName);
34
+ }
35
+ if (this.options.credentialRejected) {
36
+ throw new AdapterCredentialRejected(this.name);
37
+ }
38
+ if (this.options.credentialInvalid) {
39
+ throw new AdapterCredentialInvalid(this.name);
40
+ }
41
+ if (this.options.probeEnvVar && this.options.available === undefined) {
42
+ const env = this.options.env ?? process.env;
43
+ const v = env[this.envVarName];
44
+ if (!(typeof v === 'string' && v.trim().length > 0)) {
45
+ throw new AdapterCredentialNotConfigured(this.name, this.envVarName);
46
+ }
47
+ }
48
+ return (this.options.signals ?? []).filter((signal) => signal.sourceTimestamp >= since);
49
+ }
50
+ }
51
+ //# sourceMappingURL=support-ticket.js.map
@@ -0,0 +1,205 @@
1
+ /**
2
+ * RFC-0030 Phase 2 — Signal classifier.
3
+ *
4
+ * Classifies raw signals on three deterministic axes and applies the
5
+ * English-only language gate (OQ-13.2 resolution):
6
+ *
7
+ * 1. **Tier** — enterprise / mid / smb / free / churned (metadata-driven).
8
+ * Inference order: adapter-provided `customerTier` → customerId lookup →
9
+ * adapter's `defaultTier` (mapped to smb for Tier 1, free for Tier 2).
10
+ *
11
+ * 2. **ICP resonance** — strong / partial / weak (BM25 default; embedding
12
+ * when RFC-0019 adapter is configured — currently always BM25 in v1).
13
+ * For v1 the classifier receives an explicit `icpSegments` list from the
14
+ * caller. BM25 matching measures how closely the signal payload matches
15
+ * the declared ICP segments.
16
+ *
17
+ * 3. **Recency decay** — exponential decay: exp(-age_days × ln(2) / half_life).
18
+ * Applied at scoring time so the pipeline doesn't need to re-compute as
19
+ * time passes (age is passed as input). Half-life is read from config
20
+ * (default 30 days).
21
+ *
22
+ * **Language gate** (OQ-13.2 v0.3 re-walkthrough): non-accepted-language signal
23
+ * payloads are detected via the `franc` library (deterministic; <10ms per
24
+ * signal; JS-native; MIT-licensed; 95%+ accuracy on text >50 chars; returns
25
+ * ISO 639-3 codes). Signals in unsupported languages are dropped and logged as
26
+ * `Decision: signal-language-unsupported`. Per-org `acceptedLanguages` config
27
+ * is respected (default `['en']`); multi-language opt-in (e.g. `['en', 'fr',
28
+ * 'es']`) accepts those languages knowingly — adopters take on documented BM25
29
+ * quality degradation (~15-30% precision drop without per-language stopwords)
30
+ * in exchange for non-English signal coverage.
31
+ *
32
+ * All multipliers and weights are read from `SignalIngestionConfig`; they are
33
+ * NOT hardcoded, satisfying AC #6.
34
+ *
35
+ * @module signal-ingestion/classifier
36
+ */
37
+ import type { CustomerTier, RawSignal, SignalTier } from './types.js';
38
+ import type { SignalIngestionConfig, TierMultipliers, IcpResonanceWeights } from './config.js';
39
+ export type ICPResonance = 'strong' | 'partial' | 'weak';
40
+ /** The result of classifying a single raw signal on all three axes. */
41
+ export interface ClassifiedSignal {
42
+ /** Original signal, unmodified. */
43
+ signal: RawSignal;
44
+ /** Resolved customer tier (enterprise / mid / smb / free / churned). */
45
+ customerTier: CustomerTier;
46
+ /** ICP resonance level. */
47
+ icpResonance: ICPResonance;
48
+ /**
49
+ * Recency decay factor in [0, 1].
50
+ * 1.0 = fresh signal; approaches 0 for very old signals.
51
+ * Computed at classification time using the provided `asOf` date.
52
+ */
53
+ recencyDecay: number;
54
+ /**
55
+ * Tier multiplier looked up from config for this signal's customerTier.
56
+ * Convenience field — callers do NOT need to re-read the config.
57
+ */
58
+ tierMultiplier: number;
59
+ /**
60
+ * ICP resonance weight looked up from config.
61
+ * Convenience field.
62
+ */
63
+ icpResonanceWeight: number;
64
+ /**
65
+ * Signal-level base weight.
66
+ * - Tier 1 sources: 1.0
67
+ * - Tier 2 sources: 0.3 in Phase 2 (significance-threshold evaluation
68
+ * is Phase 4's job; Phase 4 will downgrade to 0.0 for Tier 2 sources
69
+ * below threshold, excluding them from D1 scoring).
70
+ * Phase 4/5 consumers MUST NOT rely on `baseWeight === 0` to detect
71
+ * exclusions until Phase 4 ships — Phase 2 never emits 0.0.
72
+ */
73
+ baseWeight: number;
74
+ }
75
+ /** Emitted when a signal is dropped for language reasons (RFC-0030 OQ-13.2 v0.3). */
76
+ export interface SignalLanguageUnsupportedDecision {
77
+ type: 'Decision';
78
+ decision: 'signal-language-unsupported';
79
+ sourceId: string;
80
+ /**
81
+ * Detected language as ISO 639-3 three-letter code (e.g. `'cmn'`, `'fra'`,
82
+ * `'spa'`, `'eng'`). `'und'` when text was too short or no script matched.
83
+ * For backwards compatibility with v0.2 dashboards reading `detectedScript`,
84
+ * a coarse script family hint (`'cjk'`, `'cyrillic'`, `'arabic'`, `'latin'`)
85
+ * may still be derivable from the language code via the franc data tables.
86
+ */
87
+ detectedLanguage: string;
88
+ /**
89
+ * @deprecated since v0.3 — use `detectedLanguage`. Retained for one release
90
+ * window so downstream consumers (TUI, runbook examples) can migrate.
91
+ * Populated with a coarse script-family hint derived from `detectedLanguage`
92
+ * (`'cjk'`, `'cyrillic'`, `'arabic'`, `'hebrew'`, `'devanagari'`, `'thai'`,
93
+ * `'greek'`, `'armenian'`, `'georgian'`, `'latin'`, or `'unknown'`).
94
+ */
95
+ detectedScript: string;
96
+ acceptedLanguages: string[];
97
+ message: string;
98
+ }
99
+ /** Result of classifying a batch of signals. */
100
+ export interface ClassificationResult {
101
+ /** Successfully classified signals. */
102
+ classified: ClassifiedSignal[];
103
+ /**
104
+ * Signals dropped for language reasons, emitted as Decision records per
105
+ * RFC-0030 OQ-13.2 resolution.
106
+ */
107
+ languageDecisions: SignalLanguageUnsupportedDecision[];
108
+ }
109
+ /**
110
+ * Optional customer-tier registry for resolving tier from `customerId`.
111
+ * Adopters can provide their own implementation; the pipeline falls back
112
+ * to adapter defaults when no registry is configured or when the
113
+ * `customerId` is not found.
114
+ */
115
+ export interface CustomerTierRegistry {
116
+ resolve(customerId: string): CustomerTier | undefined;
117
+ }
118
+ export interface ClassifySignalsOptions {
119
+ /**
120
+ * Point in time used to compute signal ages.
121
+ * Defaults to `new Date()` when not provided.
122
+ */
123
+ asOf?: Date;
124
+ /**
125
+ * Resolved signal ingestion configuration. Defaults to the built-in
126
+ * default config when not provided.
127
+ */
128
+ config?: SignalIngestionConfig;
129
+ /**
130
+ * Source adapter tier used for base-weight assignment when the signal does
131
+ * not carry `customerTier`. Keyed by adapter name.
132
+ *
133
+ * When not provided, base weight defaults to 1.0 (Tier 1 bias).
134
+ */
135
+ adapterTiers?: Map<string, SignalTier>;
136
+ /**
137
+ * ICP segment descriptors — the product's declared ideal customer profile.
138
+ * Each entry is a short phrase describing one ICP dimension
139
+ * (e.g. "B2B SaaS startup", "engineering team", "productivity tooling").
140
+ *
141
+ * BM25 similarity between the signal payload and these segments determines
142
+ * ICP resonance. When empty, all signals are classified as `partial`.
143
+ */
144
+ icpSegments?: string[];
145
+ /**
146
+ * Optional customer-tier registry for `customerId` lookup (tier inference
147
+ * order step 2). When absent, step 2 is skipped.
148
+ */
149
+ tierRegistry?: CustomerTierRegistry;
150
+ }
151
+ /**
152
+ * Classify a batch of raw signals.
153
+ *
154
+ * Per RFC-0030 §6 inference order for tier:
155
+ * 1. `signal.customerTier` (adapter-provided structured field)
156
+ * 2. `tierRegistry.resolve(signal.customerId)` (registry lookup)
157
+ * 3. Adapter `defaultTier` (mapped: Tier 1 → smb, Tier 2 → free)
158
+ *
159
+ * Per RFC-0030 §13.2: non-accepted-language signals are dropped and
160
+ * returned in `languageDecisions`.
161
+ */
162
+ export declare function classifySignals(signals: RawSignal[], options?: ClassifySignalsOptions): ClassificationResult;
163
+ /**
164
+ * Resolve customer tier per RFC-0030 §6.1 inference order:
165
+ * 1. `signal.customerTier`
166
+ * 2. `tierRegistry.resolve(signal.customerId)`
167
+ * 3. Adapter defaultTier → smb (Tier 1) or free (Tier 2)
168
+ */
169
+ export declare function resolveCustomerTier(signal: RawSignal, tierRegistry?: CustomerTierRegistry, adapterTiers?: Map<string, SignalTier>): CustomerTier;
170
+ /**
171
+ * Classify ICP resonance for a signal payload against the declared ICP
172
+ * segment descriptors using BM25 similarity.
173
+ *
174
+ * When `icpSegments` is empty, returns `partial` (no basis for strong/weak).
175
+ *
176
+ * BM25 parameters (k1=1.5, b=0.75) are standard.
177
+ * Score mapping:
178
+ * - ≥ 0.5 → strong
179
+ * - ≥ 0.15 → partial
180
+ * - < 0.15 → weak
181
+ */
182
+ export declare function resolveIcpResonance(payload: string, icpSegments: string[]): ICPResonance;
183
+ /** Tokenize a string to lowercase alphanumeric tokens, minimum length 2. */
184
+ export declare function tokenize(text: string): string[];
185
+ /**
186
+ * Compute exponential recency decay for a signal.
187
+ *
188
+ * `decay = exp(-age_days * ln(2) / halfLifeDays)`
189
+ *
190
+ * At age = halfLifeDays, decay = 0.5.
191
+ * At age = 0, decay = 1.0.
192
+ * Future-dated signals (age < 0) clamp to 1.0.
193
+ */
194
+ export declare function computeRecencyDecay(sourceTimestamp: Date, asOf: Date, halfLifeDays: number): number;
195
+ /**
196
+ * Compute the composite signal weight from a `ClassifiedSignal`.
197
+ *
198
+ * `weight = baseWeight × tierMultiplier × icpResonanceWeight × recencyDecay`
199
+ *
200
+ * Note: SA resonance filter (RFC-0030 §9) is applied at the cluster level in
201
+ * Phase 5, not at the signal level here.
202
+ */
203
+ export declare function computeSignalWeight(classified: ClassifiedSignal): number;
204
+ export type { TierMultipliers, IcpResonanceWeights };
205
+ //# sourceMappingURL=classifier.d.ts.map