@telora/daemon 0.18.33 → 0.18.40

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 (56) hide show
  1. package/build-info.json +6 -3
  2. package/dist/completion/event-escalations.d.ts.map +1 -1
  3. package/dist/completion/event-escalations.js +25 -17
  4. package/dist/completion/event-escalations.js.map +1 -1
  5. package/dist/completion/event.d.ts +3 -12
  6. package/dist/completion/event.d.ts.map +1 -1
  7. package/dist/completion/event.js +50 -31
  8. package/dist/completion/event.js.map +1 -1
  9. package/dist/completion/progress-snapshot.d.ts +35 -0
  10. package/dist/completion/progress-snapshot.d.ts.map +1 -0
  11. package/dist/completion/progress-snapshot.js +105 -0
  12. package/dist/completion/progress-snapshot.js.map +1 -0
  13. package/dist/listener.d.ts.map +1 -1
  14. package/dist/listener.js +9 -0
  15. package/dist/listener.js.map +1 -1
  16. package/dist/nexus-incoming.d.ts +8 -7
  17. package/dist/nexus-incoming.d.ts.map +1 -1
  18. package/dist/nexus-incoming.js +10 -12
  19. package/dist/nexus-incoming.js.map +1 -1
  20. package/dist/nexus-intake-spawn.d.ts +57 -0
  21. package/dist/nexus-intake-spawn.d.ts.map +1 -0
  22. package/dist/nexus-intake-spawn.js +158 -0
  23. package/dist/nexus-intake-spawn.js.map +1 -0
  24. package/dist/nexus-intake.d.ts +94 -0
  25. package/dist/nexus-intake.d.ts.map +1 -0
  26. package/dist/nexus-intake.js +160 -0
  27. package/dist/nexus-intake.js.map +1 -0
  28. package/dist/queries/focuses.d.ts.map +1 -1
  29. package/dist/queries/focuses.js +1 -0
  30. package/dist/queries/focuses.js.map +1 -1
  31. package/dist/queries/issues.d.ts +12 -0
  32. package/dist/queries/issues.d.ts.map +1 -1
  33. package/dist/queries/issues.js +27 -0
  34. package/dist/queries/issues.js.map +1 -1
  35. package/dist/queries/schemas.d.ts +2 -0
  36. package/dist/queries/schemas.d.ts.map +1 -1
  37. package/dist/queries/schemas.js +1 -0
  38. package/dist/queries/schemas.js.map +1 -1
  39. package/dist/spawn-sandbox.d.ts +12 -8
  40. package/dist/spawn-sandbox.d.ts.map +1 -1
  41. package/dist/spawn-sandbox.js +25 -17
  42. package/dist/spawn-sandbox.js.map +1 -1
  43. package/dist/stall-detector-config.d.ts +24 -0
  44. package/dist/stall-detector-config.d.ts.map +1 -0
  45. package/dist/stall-detector-config.js +44 -0
  46. package/dist/stall-detector-config.js.map +1 -0
  47. package/dist/stall-detectors.d.ts +108 -0
  48. package/dist/stall-detectors.d.ts.map +1 -0
  49. package/dist/stall-detectors.js +225 -0
  50. package/dist/stall-detectors.js.map +1 -0
  51. package/dist/state-cascade.d.ts.map +1 -1
  52. package/dist/state-cascade.js +21 -0
  53. package/dist/state-cascade.js.map +1 -1
  54. package/dist/types/focus.d.ts +21 -0
  55. package/dist/types/focus.d.ts.map +1 -1
  56. package/package.json +3 -3
@@ -40,6 +40,17 @@ export class SandboxUnavailableError extends Error {
40
40
  }
41
41
  /** Default bubblewrap executable name (overridable via deps for tests). */
42
42
  export const BWRAP_COMMAND = 'bwrap';
43
+ /**
44
+ * One-shot guard for the `sandbox.off_bwrap_available` hint. resolveSandboxMode
45
+ * runs inside wrapSpawnCommand on EVERY spawn, but the bwrap recommendation is a
46
+ * single calm hint, not a per-spawn alarm -- so the health event is emitted at
47
+ * most once per process. Reset via resetBwrapHintForTests() in tests.
48
+ */
49
+ let bwrapHintEmitted = false;
50
+ /** Test-only: reset the one-shot bwrap-hint guard so emission can be re-exercised. */
51
+ export function resetBwrapHintForTests() {
52
+ bwrapHintEmitted = false;
53
+ }
43
54
  /** Return true if an executable name/path exists on the host PATH. */
44
55
  export function commandOnPath(command, env = process.env) {
45
56
  if (isAbsolute(command) || command.includes('/'))
@@ -59,8 +70,10 @@ export function isBwrapAvailable(deps = {}) {
59
70
  * - explicit config.sandbox.mode wins (and is honored even if unavailable --
60
71
  * buildSandboxCommand then fail-closes).
61
72
  * - unset + darwin => 'off' (dev; bwrap is Linux-only).
62
- * - unset + linux => 'off' (safe default), with a warning health event emitted
63
- * when bwrap is available. Operators opt in explicitly after per-host validation
73
+ * - unset + linux => 'off' (safe default), with a ONE-SHOT `info` health event
74
+ * emitted (once per process) when bwrap is available. `info` keeps health
75
+ * "healthy" (only unresolved warn/error degrade it) while staying visible on
76
+ * the card. Operators opt in explicitly after per-host validation
64
77
  * -- see docs/runbook-daemon-service-user.md.
65
78
  */
66
79
  export function resolveSandboxMode(sandbox, deps = {}) {
@@ -77,9 +90,11 @@ export function resolveSandboxMode(sandbox, deps = {}) {
77
90
  if (deps.logRecommendation) {
78
91
  deps.logRecommendation(message);
79
92
  }
80
- else {
93
+ else if (!bwrapHintEmitted) {
94
+ // One-shot: emit a single calm info hint per process, not per spawn.
95
+ bwrapHintEmitted = true;
81
96
  healthEvents.emit({
82
- severity: 'warn',
97
+ severity: 'info',
83
98
  source: 'spawn-sandbox',
84
99
  code: 'sandbox.off_bwrap_available',
85
100
  message,
@@ -91,9 +106,10 @@ export function resolveSandboxMode(sandbox, deps = {}) {
91
106
  /**
92
107
  * Build the (possibly sandbox-wrapped) command + args for a spawn.
93
108
  *
94
- * mode 'off' -> returns the inner command unchanged and emits a loud
95
- * "running unconfined" signal (the spawn has the daemon user's
96
- * full authority).
109
+ * mode 'off' -> returns the inner command unchanged. The unconfined default is
110
+ * the normal posture (not an operational fault), so it emits NO
111
+ * health event -- a host where confinement cannot be enabled stays
112
+ * silent and healthy.
97
113
  * mode 'bwrap' -> if bwrap is unavailable, THROWS SandboxUnavailableError
98
114
  * (fail-closed); otherwise returns a bwrap invocation that binds
99
115
  * the worktree read-write, the readOnlyPaths read-only, system
@@ -103,7 +119,6 @@ export function buildSandboxCommand(opts) {
103
119
  const { command, args, policy, mode, bwrapAvailable } = opts;
104
120
  const bwrap = opts.bwrapCommand ?? BWRAP_COMMAND;
105
121
  if (mode === 'off') {
106
- opts.onUnconfined?.('spawn sandbox is OFF -- the agent runs with the daemon user\'s full filesystem authority');
107
122
  return { command, args: [...args] };
108
123
  }
109
124
  // mode === 'bwrap'
@@ -151,7 +166,8 @@ export function buildSandboxCommand(opts) {
151
166
  /**
152
167
  * High-level helper used by the spawn path: resolve mode from config, derive the
153
168
  * read-only allowlist (toolchain + model-auth config), and build the wrapped
154
- * command. Emits a health event on unconfined runs and refuses (throws) when an
169
+ * command. resolveSandboxMode emits the one-shot `info` bwrap hint; the
170
+ * unconfined ('off') path emits NO health event. Refuses (throws) when an
155
171
  * explicitly-required sandbox is unavailable.
156
172
  */
157
173
  export function wrapSpawnCommand(config, spawn) {
@@ -164,14 +180,6 @@ export function wrapSpawnCommand(config, spawn) {
164
180
  policy: { worktreePath: spawn.worktreePath, readOnlyPaths },
165
181
  mode,
166
182
  bwrapAvailable,
167
- onUnconfined: (reason) => {
168
- healthEvents.emit({
169
- severity: 'warn',
170
- source: 'spawn-sandbox',
171
- code: 'sandbox.unconfined',
172
- message: reason,
173
- });
174
- },
175
183
  });
176
184
  }
177
185
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"spawn-sandbox.js","sourceRoot":"","sources":["../src/spawn-sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spawn-sandbox.js","sourceRoot":"","sources":["../src/spawn-sandbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Single in-daemon source for the two stall-detector thresholds.
3
+ *
4
+ * The CANONICAL declarations (Zod schema, ENV_VAR_METADATA discoverability
5
+ * entry, default, description) live in packages/daemon-core/src/env-config.ts --
6
+ * that module is the source of truth across the system. They are resolved here
7
+ * directly from process.env rather than through loadEnvConfig() for the same
8
+ * reason listener-auto-advance.ts inlines its daemon-core constants: the daemon
9
+ * consumes the registry-PUBLISHED daemon-core, so a typed
10
+ * loadEnvConfig().TELORA_VERIFY_FAILED_ITERATION_CAP access cannot compile until
11
+ * a daemon-core publish carrying the new keys lands. Centralizing the two reads
12
+ * + defaults in ONE module (instead of duplicating inline defaults at each call
13
+ * site) keeps the daemon-side mirror single-sourced; the names + defaults below
14
+ * MUST stay in sync with env-config.ts (positiveInt(5) / positiveFloat(6)).
15
+ */
16
+ /** Default for TELORA_VERIFY_FAILED_ITERATION_CAP (mirror of env-config positiveInt(5)). */
17
+ export declare const VERIFY_FAILED_ITERATION_CAP_DEFAULT = 5;
18
+ /** Default for TELORA_REVIEW_WINDOW_STALE_HOURS (mirror of env-config positiveFloat(6)). */
19
+ export declare const REVIEW_WINDOW_STALE_HOURS_DEFAULT = 6;
20
+ /** Resolve the verify_failed re-code cap (env override or default). */
21
+ export declare function resolveVerifyFailedCap(): number;
22
+ /** Resolve the review-window staleness threshold in hours (env override or default). */
23
+ export declare function resolveReviewWindowStaleHours(): number;
24
+ //# sourceMappingURL=stall-detector-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-detector-config.d.ts","sourceRoot":"","sources":["../src/stall-detector-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,mCAAmC,IAAI,CAAC;AACrD,4FAA4F;AAC5F,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAkBnD,uEAAuE;AACvE,wBAAgB,sBAAsB,IAAI,MAAM,CAE/C;AAED,wFAAwF;AACxF,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Single in-daemon source for the two stall-detector thresholds.
3
+ *
4
+ * The CANONICAL declarations (Zod schema, ENV_VAR_METADATA discoverability
5
+ * entry, default, description) live in packages/daemon-core/src/env-config.ts --
6
+ * that module is the source of truth across the system. They are resolved here
7
+ * directly from process.env rather than through loadEnvConfig() for the same
8
+ * reason listener-auto-advance.ts inlines its daemon-core constants: the daemon
9
+ * consumes the registry-PUBLISHED daemon-core, so a typed
10
+ * loadEnvConfig().TELORA_VERIFY_FAILED_ITERATION_CAP access cannot compile until
11
+ * a daemon-core publish carrying the new keys lands. Centralizing the two reads
12
+ * + defaults in ONE module (instead of duplicating inline defaults at each call
13
+ * site) keeps the daemon-side mirror single-sourced; the names + defaults below
14
+ * MUST stay in sync with env-config.ts (positiveInt(5) / positiveFloat(6)).
15
+ */
16
+ /** Default for TELORA_VERIFY_FAILED_ITERATION_CAP (mirror of env-config positiveInt(5)). */
17
+ export const VERIFY_FAILED_ITERATION_CAP_DEFAULT = 5;
18
+ /** Default for TELORA_REVIEW_WINDOW_STALE_HOURS (mirror of env-config positiveFloat(6)). */
19
+ export const REVIEW_WINDOW_STALE_HOURS_DEFAULT = 6;
20
+ /** Parse a positive-int env var, falling back to the default on absent/invalid. */
21
+ function envPositiveInt(name, def) {
22
+ const raw = process.env[name];
23
+ if (raw === undefined || raw === '')
24
+ return def;
25
+ const n = Number(raw);
26
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : def;
27
+ }
28
+ /** Parse a positive-float env var, falling back to the default on absent/invalid. */
29
+ function envPositiveFloat(name, def) {
30
+ const raw = process.env[name];
31
+ if (raw === undefined || raw === '')
32
+ return def;
33
+ const n = Number(raw);
34
+ return Number.isFinite(n) && n > 0 ? n : def;
35
+ }
36
+ /** Resolve the verify_failed re-code cap (env override or default). */
37
+ export function resolveVerifyFailedCap() {
38
+ return envPositiveInt('TELORA_VERIFY_FAILED_ITERATION_CAP', VERIFY_FAILED_ITERATION_CAP_DEFAULT);
39
+ }
40
+ /** Resolve the review-window staleness threshold in hours (env override or default). */
41
+ export function resolveReviewWindowStaleHours() {
42
+ return envPositiveFloat('TELORA_REVIEW_WINDOW_STALE_HOURS', REVIEW_WINDOW_STALE_HOURS_DEFAULT);
43
+ }
44
+ //# sourceMappingURL=stall-detector-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-detector-config.js","sourceRoot":"","sources":["../src/stall-detector-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4FAA4F;AAC5F,MAAM,CAAC,MAAM,mCAAmC,GAAG,CAAC,CAAC;AACrD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD,mFAAmF;AACnF,SAAS,cAAc,CAAC,IAAY,EAAE,GAAW;IAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED,qFAAqF;AACrF,SAAS,gBAAgB,CAAC,IAAY,EAAE,GAAW;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAC/C,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,sBAAsB;IACpC,OAAO,cAAc,CAAC,oCAAoC,EAAE,mCAAmC,CAAC,CAAC;AACnG,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,6BAA6B;IAC3C,OAAO,gBAAgB,CAAC,kCAAkC,EAAE,iCAAiC,CAAC,CAAC;AACjG,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Targeted daemon-owned stall detectors.
3
+ *
4
+ * The lifecycle-aware no-progress predicate (completion/event.ts) makes the
5
+ * generic watchdog PATIENT: it no longer trips on healthy review/verify/requeue
6
+ * latency. That patience is only safe if the genuinely-wedged daemon-owned
7
+ * cases it would otherwise have (noisily) surfaced are caught by dedicated
8
+ * detectors with their own actionable reasons. This module is that safety
9
+ * backfill:
10
+ *
11
+ * 1. verify_failed cycle cap -- a delivery whose verify_failed re-code cycle
12
+ * count (iteration_count) exceeds TELORA_VERIFY_FAILED_ITERATION_CAP is
13
+ * stuck in an unbounded re-code loop the verify gate will never clear. File
14
+ * ONE verify_gate_unclearable escalation (needs_operator) carrying the
15
+ * gate_state / last failing condition -- not a generic no-progress ping.
16
+ *
17
+ * 2. review-window staleness -- a focus stuck in the `reviewing` phase whose
18
+ * review_requested_at has sat past TELORA_REVIEW_WINDOW_STALE_HOURS with no
19
+ * active review team (a completed review would have cleared
20
+ * review_requested_at) is wedged. File ONE review_window_stalled escalation
21
+ * (needs_operator).
22
+ *
23
+ * BOTH detectors are PER-FOCUS and operate on state the caller already has --
24
+ * they do NOT fan out their own getActiveFocuses / getFocusDeliveries pass.
25
+ * They are driven from state-cascade.ts's runCascadeChecks loop, which already
26
+ * iterates active focuses, fetches each focus's deliveries, and computes the
27
+ * review phase: the detectors RIDE that existing review-handling seam (so there
28
+ * is no redundant per-poll fan-out, and the review-window detector is wired into
29
+ * the same review handling that owns review_requested_at). Each files AT MOST
30
+ * ONE open escalation per entity (per delivery / per focus), mirroring the
31
+ * merge-back stuck_focus_branch idempotency: the dedup queries the DB so it
32
+ * survives a daemon restart. Pure over injected deps so the four cases
33
+ * (under-cap, over-cap, fresh window, stale window) unit-test without
34
+ * Supabase/MCP.
35
+ */
36
+ import type { FocusDeliveryInfo } from './types.js';
37
+ import { type CreateEscalationParams } from './queries/issues.js';
38
+ /**
39
+ * escalation_kind discriminators for the two stall detectors. Declared as
40
+ * daemon-local string constants (not imported from @telora/daemon-core's
41
+ * ESCALATION_KINDS) on purpose: the daemon consumes the registry-PUBLISHED
42
+ * daemon-core, so it cannot reference newly-added enum members until a build
43
+ * that depends on a daemon-core publish carrying them. They ARE now in
44
+ * daemon-core (ESCALATION_KINDS.VERIFY_GATE_UNCLEARABLE / .REVIEW_WINDOW_STALLED)
45
+ * for other consumers, but the daemon keeps the local strings so this code does
46
+ * not depend on publish ordering. The DB CHECK -- loosened by the
47
+ * agent_escalations_kind_stall_detectors migration -- is the real constraint.
48
+ */
49
+ export declare const VERIFY_GATE_UNCLEARABLE_KIND = "verify_gate_unclearable";
50
+ export declare const REVIEW_WINDOW_STALLED_KIND = "review_window_stalled";
51
+ /** The per-focus context both detectors need to anchor an escalation. */
52
+ export interface FocusStallContext {
53
+ organizationId: string;
54
+ focusId: string;
55
+ focusName: string;
56
+ }
57
+ export interface VerifyFailedCapDeps {
58
+ hasOpenEscalationForDelivery: (deliveryId: string, escalationKind: string) => Promise<boolean>;
59
+ createEscalation: (params: CreateEscalationParams) => Promise<{
60
+ escalationId: string | null;
61
+ } | void>;
62
+ /** Override the cap for tests; defaults to the resolved env-config value. */
63
+ cap?: number;
64
+ }
65
+ /**
66
+ * Pure decision: does this delivery exceed the verify_failed re-code cap?
67
+ * A terminal delivery (done/cancelled) is never escalated -- it has stopped
68
+ * cycling. Exported for direct unit testing.
69
+ */
70
+ export declare function exceedsVerifyFailedCap(delivery: Pick<FocusDeliveryInfo, 'executionStatus' | 'iterationCount'>, cap: number): boolean;
71
+ /**
72
+ * Check ONE focus's ALREADY-FETCHED deliveries for any whose iteration_count
73
+ * exceeds the verify_failed re-code cap, filing a verify_gate_unclearable
74
+ * escalation (needs_operator) for each -- at most one open per delivery.
75
+ * Operates on the deliveries the caller (runCascadeChecks) already loaded; it
76
+ * does NOT fetch.
77
+ */
78
+ export declare function checkVerifyFailedCapForFocus(ctx: FocusStallContext, deliveries: FocusDeliveryInfo[], deps?: VerifyFailedCapDeps): Promise<void>;
79
+ export interface ReviewWindowDeps {
80
+ hasOpenFocusEscalation: (focusId: string, escalationKind: string) => Promise<boolean>;
81
+ createEscalation: (params: CreateEscalationParams) => Promise<{
82
+ escalationId: string | null;
83
+ } | void>;
84
+ /** Focus IDs that currently have an active REVIEW team. Defaults to live teams. */
85
+ activeReviewFocusIds: () => Set<string>;
86
+ /** Current epoch ms; injectable for tests. Defaults to Date.now. */
87
+ now: () => number;
88
+ /** Override the staleness threshold (hours) for tests; defaults to env-config. */
89
+ thresholdHours?: number;
90
+ }
91
+ /** Focus IDs that currently have an active REVIEW team (live default). */
92
+ export declare function activeReviewFocusIds(): Set<string>;
93
+ /**
94
+ * Pure decision: is this focus's review window stale? True when a
95
+ * review_requested_at exists, is older than `thresholdMs`, and the focus has no
96
+ * active review team. A completed review clears review_requested_at, so a
97
+ * still-set timestamp older than the threshold with no active review team means
98
+ * the review never ran (or never finished). Exported for direct unit testing.
99
+ */
100
+ export declare function isReviewWindowStale(reviewRequestedAt: string | null, hasActiveReview: boolean, nowMs: number, thresholdMs: number): boolean;
101
+ /**
102
+ * Check ONE focus for a stale review window and file a review_window_stalled
103
+ * escalation (needs_operator) -- at most one open per focus. The caller
104
+ * (runCascadeChecks) passes the focus's review_requested_at, so this rides the
105
+ * existing review-handling seam rather than fanning out its own focus scan.
106
+ */
107
+ export declare function checkReviewWindowStaleForFocus(ctx: FocusStallContext, reviewRequestedAt: string | null, deps?: ReviewWindowDeps): Promise<void>;
108
+ //# sourceMappingURL=stall-detectors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-detectors.d.ts","sourceRoot":"","sources":["../src/stall-detectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAMpD,OAAO,EAIL,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAK7B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AACtE,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAElE,yEAAyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD,MAAM,WAAW,mBAAmB;IAClC,4BAA4B,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/F,gBAAgB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACtG,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAOD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,gBAAgB,CAAC,EACvE,GAAG,EAAE,MAAM,GACV,OAAO,CAGT;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,iBAAiB,EACtB,UAAU,EAAE,iBAAiB,EAAE,EAC/B,IAAI,GAAE,mBAAgD,GACrD,OAAO,CAAC,IAAI,CAAC,CA0Df;AAID,MAAM,WAAW,gBAAgB;IAC/B,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,gBAAgB,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACtG,mFAAmF;IACnF,oBAAoB,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,oEAAoE;IACpE,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,kFAAkF;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAMlD;AASD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,eAAe,EAAE,OAAO,EACxB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAClB,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,iBAAiB,EACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,gBAA0C,GAC/C,OAAO,CAAC,IAAI,CAAC,CA8Df"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Targeted daemon-owned stall detectors.
3
+ *
4
+ * The lifecycle-aware no-progress predicate (completion/event.ts) makes the
5
+ * generic watchdog PATIENT: it no longer trips on healthy review/verify/requeue
6
+ * latency. That patience is only safe if the genuinely-wedged daemon-owned
7
+ * cases it would otherwise have (noisily) surfaced are caught by dedicated
8
+ * detectors with their own actionable reasons. This module is that safety
9
+ * backfill:
10
+ *
11
+ * 1. verify_failed cycle cap -- a delivery whose verify_failed re-code cycle
12
+ * count (iteration_count) exceeds TELORA_VERIFY_FAILED_ITERATION_CAP is
13
+ * stuck in an unbounded re-code loop the verify gate will never clear. File
14
+ * ONE verify_gate_unclearable escalation (needs_operator) carrying the
15
+ * gate_state / last failing condition -- not a generic no-progress ping.
16
+ *
17
+ * 2. review-window staleness -- a focus stuck in the `reviewing` phase whose
18
+ * review_requested_at has sat past TELORA_REVIEW_WINDOW_STALE_HOURS with no
19
+ * active review team (a completed review would have cleared
20
+ * review_requested_at) is wedged. File ONE review_window_stalled escalation
21
+ * (needs_operator).
22
+ *
23
+ * BOTH detectors are PER-FOCUS and operate on state the caller already has --
24
+ * they do NOT fan out their own getActiveFocuses / getFocusDeliveries pass.
25
+ * They are driven from state-cascade.ts's runCascadeChecks loop, which already
26
+ * iterates active focuses, fetches each focus's deliveries, and computes the
27
+ * review phase: the detectors RIDE that existing review-handling seam (so there
28
+ * is no redundant per-poll fan-out, and the review-window detector is wired into
29
+ * the same review handling that owns review_requested_at). Each files AT MOST
30
+ * ONE open escalation per entity (per delivery / per focus), mirroring the
31
+ * merge-back stuck_focus_branch idempotency: the dedup queries the DB so it
32
+ * survives a daemon restart. Pure over injected deps so the four cases
33
+ * (under-cap, over-cap, fresh window, stale window) unit-test without
34
+ * Supabase/MCP.
35
+ */
36
+ import { ESCALATION_REASONS, ESCALATION_DISPOSITIONS, withRetry, } from '@telora/daemon-core';
37
+ import { createEscalation as defaultCreateEscalation, hasOpenEscalationForDelivery as defaultHasOpenEscalationForDelivery, hasOpenFocusEscalation as defaultHasOpenFocusEscalation, } from './queries/issues.js';
38
+ import { getActiveTeams } from './focus-executor.js';
39
+ import { isStatusTerminal } from './stage-classifier.js';
40
+ import { resolveVerifyFailedCap, resolveReviewWindowStaleHours } from './stall-detector-config.js';
41
+ /**
42
+ * escalation_kind discriminators for the two stall detectors. Declared as
43
+ * daemon-local string constants (not imported from @telora/daemon-core's
44
+ * ESCALATION_KINDS) on purpose: the daemon consumes the registry-PUBLISHED
45
+ * daemon-core, so it cannot reference newly-added enum members until a build
46
+ * that depends on a daemon-core publish carrying them. They ARE now in
47
+ * daemon-core (ESCALATION_KINDS.VERIFY_GATE_UNCLEARABLE / .REVIEW_WINDOW_STALLED)
48
+ * for other consumers, but the daemon keeps the local strings so this code does
49
+ * not depend on publish ordering. The DB CHECK -- loosened by the
50
+ * agent_escalations_kind_stall_detectors migration -- is the real constraint.
51
+ */
52
+ export const VERIFY_GATE_UNCLEARABLE_KIND = 'verify_gate_unclearable';
53
+ export const REVIEW_WINDOW_STALLED_KIND = 'review_window_stalled';
54
+ /** Render gate_state compactly for the escalation body. */
55
+ function formatGateState(gateState) {
56
+ if (gateState == null)
57
+ return '(none recorded)';
58
+ try {
59
+ return JSON.stringify(gateState);
60
+ }
61
+ catch {
62
+ return '(unserializable)';
63
+ }
64
+ }
65
+ const defaultVerifyFailedCapDeps = {
66
+ hasOpenEscalationForDelivery: defaultHasOpenEscalationForDelivery,
67
+ createEscalation: defaultCreateEscalation,
68
+ };
69
+ /**
70
+ * Pure decision: does this delivery exceed the verify_failed re-code cap?
71
+ * A terminal delivery (done/cancelled) is never escalated -- it has stopped
72
+ * cycling. Exported for direct unit testing.
73
+ */
74
+ export function exceedsVerifyFailedCap(delivery, cap) {
75
+ if (isStatusTerminal(delivery.executionStatus ?? ''))
76
+ return false;
77
+ return (delivery.iterationCount ?? 0) > cap;
78
+ }
79
+ /**
80
+ * Check ONE focus's ALREADY-FETCHED deliveries for any whose iteration_count
81
+ * exceeds the verify_failed re-code cap, filing a verify_gate_unclearable
82
+ * escalation (needs_operator) for each -- at most one open per delivery.
83
+ * Operates on the deliveries the caller (runCascadeChecks) already loaded; it
84
+ * does NOT fetch.
85
+ */
86
+ export async function checkVerifyFailedCapForFocus(ctx, deliveries, deps = defaultVerifyFailedCapDeps) {
87
+ const cap = deps.cap ?? resolveVerifyFailedCap();
88
+ for (const delivery of deliveries) {
89
+ if (!exceedsVerifyFailedCap(delivery, cap))
90
+ continue;
91
+ try {
92
+ if (await deps.hasOpenEscalationForDelivery(delivery.id, VERIFY_GATE_UNCLEARABLE_KIND)) {
93
+ continue;
94
+ }
95
+ await withRetry(() => deps.createEscalation({
96
+ organizationId: ctx.organizationId,
97
+ sessionId: '',
98
+ issueId: null,
99
+ reasonType: ESCALATION_REASONS.BLOCKED_BY_EXTERNAL,
100
+ // Operator triages the unclearable gate condition from gate_state.
101
+ disposition: ESCALATION_DISPOSITIONS.NEEDS_OPERATOR,
102
+ anchor: { deliveryId: delivery.id, focusId: ctx.focusId },
103
+ escalationKind: VERIFY_GATE_UNCLEARABLE_KIND,
104
+ description: `Delivery "${delivery.name}" has been through ${delivery.iterationCount ?? 0} ` +
105
+ `verify_failed re-code cycles (cap ${cap}) without clearing the verify gate. ` +
106
+ `The gate keeps rejecting the work -- the loop will not converge on its own.\n\n` +
107
+ `**Focus:** ${ctx.focusName}\n` +
108
+ `**Focus ID:** ${ctx.focusId}\n` +
109
+ `**Delivery:** "${delivery.name}" (${delivery.id})\n` +
110
+ `**Status:** ${delivery.executionStatus ?? 'unknown'}\n` +
111
+ `**iteration_count:** ${delivery.iterationCount ?? 0}\n` +
112
+ `**gate_state:** ${formatGateState(delivery.gateState)}\n\n` +
113
+ `Inspect the gate_state / last failing condition above. See docs/runbook-verify-gate.md.`,
114
+ whatWasTried: `The delivery re-entered verify_failed ${delivery.iterationCount ?? 0} times; ` +
115
+ `each verify pass was rejected by the gate and re-queued.`,
116
+ helpNeeded: `Resolve the blocking gate condition (relax the gate, fix the underlying defect ` +
117
+ `manually, or cancel the delivery if it cannot pass).`,
118
+ metadata: {
119
+ cause: VERIFY_GATE_UNCLEARABLE_KIND,
120
+ deliveryName: delivery.name,
121
+ executionStatus: delivery.executionStatus,
122
+ iterationCount: delivery.iterationCount ?? 0,
123
+ cap,
124
+ gateState: delivery.gateState ?? null,
125
+ },
126
+ }), { maxAttempts: 3, label: `escalation-verify-gate-unclearable-${delivery.id}` });
127
+ console.log(`[stall-detectors] Filed verify_gate_unclearable escalation for delivery "${delivery.name}" ` +
128
+ `(iteration_count ${delivery.iterationCount ?? 0} > cap ${cap})`);
129
+ }
130
+ catch (err) {
131
+ console.warn(`[stall-detectors] verify-failed-cap: failed to file escalation for delivery "${delivery.name}":`, err.message);
132
+ }
133
+ }
134
+ }
135
+ /** Focus IDs that currently have an active REVIEW team (live default). */
136
+ export function activeReviewFocusIds() {
137
+ const ids = new Set();
138
+ for (const [focusId, team] of getActiveTeams()) {
139
+ if (team.sessionType === 'review')
140
+ ids.add(focusId);
141
+ }
142
+ return ids;
143
+ }
144
+ const defaultReviewWindowDeps = {
145
+ hasOpenFocusEscalation: defaultHasOpenFocusEscalation,
146
+ createEscalation: defaultCreateEscalation,
147
+ activeReviewFocusIds,
148
+ now: () => Date.now(),
149
+ };
150
+ /**
151
+ * Pure decision: is this focus's review window stale? True when a
152
+ * review_requested_at exists, is older than `thresholdMs`, and the focus has no
153
+ * active review team. A completed review clears review_requested_at, so a
154
+ * still-set timestamp older than the threshold with no active review team means
155
+ * the review never ran (or never finished). Exported for direct unit testing.
156
+ */
157
+ export function isReviewWindowStale(reviewRequestedAt, hasActiveReview, nowMs, thresholdMs) {
158
+ if (!reviewRequestedAt)
159
+ return false;
160
+ if (hasActiveReview)
161
+ return false;
162
+ const requestedMs = Date.parse(reviewRequestedAt);
163
+ if (Number.isNaN(requestedMs))
164
+ return false;
165
+ return nowMs - requestedMs > thresholdMs;
166
+ }
167
+ /**
168
+ * Check ONE focus for a stale review window and file a review_window_stalled
169
+ * escalation (needs_operator) -- at most one open per focus. The caller
170
+ * (runCascadeChecks) passes the focus's review_requested_at, so this rides the
171
+ * existing review-handling seam rather than fanning out its own focus scan.
172
+ */
173
+ export async function checkReviewWindowStaleForFocus(ctx, reviewRequestedAt, deps = defaultReviewWindowDeps) {
174
+ const thresholdHours = deps.thresholdHours ?? resolveReviewWindowStaleHours();
175
+ const thresholdMs = thresholdHours * 3_600_000;
176
+ const nowMs = deps.now();
177
+ const hasActiveReview = deps.activeReviewFocusIds().has(ctx.focusId);
178
+ if (!isReviewWindowStale(reviewRequestedAt, hasActiveReview, nowMs, thresholdMs)) {
179
+ return;
180
+ }
181
+ try {
182
+ if (await deps.hasOpenFocusEscalation(ctx.focusId, REVIEW_WINDOW_STALLED_KIND)) {
183
+ return;
184
+ }
185
+ const ageHours = Math.floor((nowMs - Date.parse(reviewRequestedAt)) / 3_600_000);
186
+ await withRetry(() => deps.createEscalation({
187
+ organizationId: ctx.organizationId,
188
+ sessionId: '',
189
+ issueId: null,
190
+ reasonType: ESCALATION_REASONS.BLOCKED_BY_EXTERNAL,
191
+ // Operator triages a review that was requested but never ran/finished.
192
+ disposition: ESCALATION_DISPOSITIONS.NEEDS_OPERATOR,
193
+ anchor: { focusId: ctx.focusId },
194
+ escalationKind: REVIEW_WINDOW_STALLED_KIND,
195
+ description: `Focus "${ctx.focusName}" requested a review ~${ageHours}h ago ` +
196
+ `(threshold ${thresholdHours}h) but no review is running and none has completed ` +
197
+ `(a completed review clears review_requested_at). The focus is wedged in the ` +
198
+ `review window -- its verify deliveries will never route to done/verify_failed ` +
199
+ `on their own.\n\n` +
200
+ `**Focus:** ${ctx.focusName}\n` +
201
+ `**Focus ID:** ${ctx.focusId}\n` +
202
+ `**review_requested_at:** ${reviewRequestedAt}\n\n` +
203
+ `Either re-spawn the review (clear and re-request the review window) or clear the ` +
204
+ `stale request via telora_product_focus update clearReviewRequest=true.`,
205
+ whatWasTried: `The review window was opened (review_requested_at set ~${ageHours}h ago) but no ` +
206
+ `review team is active and review_requested_at was never cleared by a completion.`,
207
+ helpNeeded: `Re-trigger the review or clear the stale review_requested_at ` +
208
+ `(telora_product_focus update clearReviewRequest=true) so the verify deliveries ` +
209
+ `can route forward.`,
210
+ metadata: {
211
+ cause: REVIEW_WINDOW_STALLED_KIND,
212
+ focusName: ctx.focusName,
213
+ reviewRequestedAt,
214
+ ageHours,
215
+ thresholdHours,
216
+ },
217
+ }), { maxAttempts: 3, label: `escalation-review-window-stalled-${ctx.focusId}` });
218
+ console.log(`[stall-detectors] Filed review_window_stalled escalation for focus "${ctx.focusName}" ` +
219
+ `(review_requested_at ~${ageHours}h old > ${thresholdHours}h)`);
220
+ }
221
+ catch (err) {
222
+ console.warn(`[stall-detectors] review-window: failed to file escalation for focus "${ctx.focusName}":`, err.message);
223
+ }
224
+ }
225
+ //# sourceMappingURL=stall-detectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stall-detectors.js","sourceRoot":"","sources":["../src/stall-detectors.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"state-cascade.d.ts","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAG9E,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACtE,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAgCD;;;GAGG;AACH,wBAAgB,iCAAiC,IAAI,IAAI,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,cAAc,EAAE,cAAc,GAAG,IAAI,EACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,UAAU,EAAE,iBAAiB,EAAE,EAC/B,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsC1E"}
1
+ {"version":3,"file":"state-cascade.d.ts","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AASpD,OAAO,EAAoB,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAI9E,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACtE,oBAAoB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,sBAAsB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAgCD;;;GAGG;AACH,wBAAgB,iCAAiC,IAAI,IAAI,CAExD;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,cAAc,EAAE,cAAc,GAAG,IAAI,EACrC,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,UAAU,EAAE,iBAAiB,EAAE,EAC/B,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,mBAAmB,EAC1B,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,IAAI,GAAE,WAAyB,GAC9B,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D1E"}
@@ -34,6 +34,7 @@ import { configForProduct } from './config.js';
34
34
  import { emitLoopTrigger } from './loop-event-bus.js';
35
35
  import { deriveFocusPhase } from './focus-phase.js';
36
36
  import { isReviewEnabled } from './pipeline-config.js';
37
+ import { checkVerifyFailedCapForFocus, checkReviewWindowStaleForFocus } from './stall-detectors.js';
37
38
  const defaultDeps = {
38
39
  getFocusDeliveries: _getFocusDeliveries,
39
40
  setReviewRequestedAt: _setReviewRequestedAt,
@@ -165,6 +166,26 @@ export async function runCascadeChecks(config) {
165
166
  });
166
167
  await checkAutoReview(focus.focus_id, phase, focus.pipeline_config, focus.review_requested_at, deliveries);
167
168
  await clearStaleReviewRequest(focus.focus_id, phase, focus.review_requested_at);
169
+ // Targeted daemon-owned stall detectors RIDE this same review-handling
170
+ // seam: they reuse the deliveries + review phase + review_requested_at
171
+ // already loaded above instead of fanning out a second per-poll
172
+ // getActiveFocuses/getFocusDeliveries pass.
173
+ const stallCtx = {
174
+ organizationId: pc.organizationId,
175
+ focusId: focus.focus_id,
176
+ focusName: focus.focus_name,
177
+ };
178
+ // (1) verify_failed re-code cap -> verify_gate_unclearable (per delivery).
179
+ await checkVerifyFailedCapForFocus(stallCtx, deliveries);
180
+ // (2) review-window staleness -> review_window_stalled (per focus).
181
+ // Gated on the cascade's own derived `reviewing` phase: a focus stuck
182
+ // in the review window is exactly phase === 'reviewing' with
183
+ // review_requested_at still set. (clearStaleReviewRequest above only
184
+ // clears on a coding/kickoff regression, so a still-'reviewing' phase
185
+ // means the request was not cleared this tick.)
186
+ if (phase === 'reviewing') {
187
+ await checkReviewWindowStaleForFocus(stallCtx, focus.review_requested_at);
188
+ }
168
189
  }
169
190
  catch (err) {
170
191
  console.warn(`[state-cascade] Cascade pass failed for focus ${focus.focus_id}:`, err.message);
@@ -1 +1 @@
1
- {"version":3,"file":"state-cascade.js","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"state-cascade.js","sourceRoot":"","sources":["../src/state-cascade.ts"],"names":[],"mappings":"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"}