@maolon/pi-watcher 0.0.0-stage → 0.1.1

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 (72) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -2
  4. package/dist/cli.d.ts +8 -0
  5. package/dist/cli.js +369 -0
  6. package/dist/contracts/interfaces.d.ts +262 -0
  7. package/dist/contracts/interfaces.js +1 -0
  8. package/dist/contracts/policy-defaults.json +61 -0
  9. package/dist/contracts/relay-next.interfaces.d.ts +160 -0
  10. package/dist/contracts/relay-next.interfaces.js +1 -0
  11. package/dist/engine/cards.d.ts +68 -0
  12. package/dist/engine/cards.js +76 -0
  13. package/dist/engine/engine.d.ts +178 -0
  14. package/dist/engine/engine.js +1162 -0
  15. package/dist/engine/hard-rules.d.ts +53 -0
  16. package/dist/engine/hard-rules.js +96 -0
  17. package/dist/engine/semantic.d.ts +49 -0
  18. package/dist/engine/semantic.js +125 -0
  19. package/dist/engine/service.d.ts +62 -0
  20. package/dist/engine/service.js +587 -0
  21. package/dist/engine/tool-actions.d.ts +35 -0
  22. package/dist/engine/tool-actions.js +348 -0
  23. package/dist/engine/widget.d.ts +29 -0
  24. package/dist/engine/widget.js +52 -0
  25. package/dist/ipc/client.d.ts +26 -0
  26. package/dist/ipc/client.js +106 -0
  27. package/dist/ipc/server.d.ts +78 -0
  28. package/dist/ipc/server.js +105 -0
  29. package/dist/jev/client.d.ts +38 -0
  30. package/dist/jev/client.js +239 -0
  31. package/dist/jev/consent.d.ts +12 -0
  32. package/dist/jev/consent.js +37 -0
  33. package/dist/jev/index.d.ts +9 -0
  34. package/dist/jev/index.js +9 -0
  35. package/dist/jev/mock.d.ts +30 -0
  36. package/dist/jev/mock.js +77 -0
  37. package/dist/jev/pi-registry.d.ts +66 -0
  38. package/dist/jev/pi-registry.js +127 -0
  39. package/dist/jev/questions.d.ts +15 -0
  40. package/dist/jev/questions.js +76 -0
  41. package/dist/jev/sanitizer.d.ts +10 -0
  42. package/dist/jev/sanitizer.js +59 -0
  43. package/dist/jev/types.d.ts +78 -0
  44. package/dist/jev/types.js +54 -0
  45. package/dist/pi-extension.d.ts +123 -0
  46. package/dist/pi-extension.js +687 -0
  47. package/dist/relay/managed.d.ts +120 -0
  48. package/dist/relay/managed.js +482 -0
  49. package/dist/relay/negotiate.d.ts +39 -0
  50. package/dist/relay/negotiate.js +112 -0
  51. package/dist/runtime.d.ts +74 -0
  52. package/dist/runtime.js +246 -0
  53. package/dist/source/agent-check/adapter.d.ts +57 -0
  54. package/dist/source/agent-check/adapter.js +224 -0
  55. package/dist/source/agent-file/adapter.d.ts +57 -0
  56. package/dist/source/agent-file/adapter.js +217 -0
  57. package/dist/source/task-status-v1/adapter.d.ts +36 -0
  58. package/dist/source/task-status-v1/adapter.js +263 -0
  59. package/dist/source/task-status-v1/producer.d.ts +81 -0
  60. package/dist/source/task-status-v1/producer.js +127 -0
  61. package/dist/storage/lock.d.ts +14 -0
  62. package/dist/storage/lock.js +66 -0
  63. package/dist/storage/schema.sql +166 -0
  64. package/dist/storage/store.d.ts +352 -0
  65. package/dist/storage/store.js +555 -0
  66. package/dist/util/clock.d.ts +23 -0
  67. package/dist/util/clock.js +34 -0
  68. package/dist/util/ids.d.ts +8 -0
  69. package/dist/util/ids.js +29 -0
  70. package/dist/util/result.d.ts +28 -0
  71. package/dist/util/result.js +54 -0
  72. package/package.json +101 -4
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Watcher-side view of the pi-relay managed-delivery shapes (receipts, withdraw results,
3
+ * consumer responses). pi-relay does not re-export these types from its public entry points,
4
+ * so they are declared here; keep them in sync with the @maolon/pi-relay version in package.json.
5
+ */
6
+ export type Json = null | boolean | number | string | Json[] | {
7
+ [key: string]: Json;
8
+ };
9
+ export interface Event {
10
+ kind: 'event';
11
+ id: string;
12
+ type: string;
13
+ schemaVersion: number;
14
+ subject?: string;
15
+ occurredAt: string;
16
+ validUntil: string;
17
+ data: {
18
+ [key: string]: Json;
19
+ };
20
+ }
21
+ export interface Scope {
22
+ id: string;
23
+ revision: number;
24
+ }
25
+ export interface ManagedOptions {
26
+ audienceRef: string;
27
+ scope: Scope;
28
+ consumerProfileId: string;
29
+ requestedMode: 'display' | 'resume';
30
+ }
31
+ export interface RouteReceipt {
32
+ routeRef: string;
33
+ targetRevision: number;
34
+ targetUpdatedAt?: string;
35
+ freshness: 'fresh' | 'stale' | 'offline';
36
+ admission: 'accepted' | 'staged' | 'rejected' | 'unknown';
37
+ delivery: 'pending' | 'held' | 'intent' | 'submitted' | 'recorded' | 'unknown' | 'suppressed' | 'expired' | 'withdrawn';
38
+ withdrawal: 'none' | 'pending' | 'prevented' | 'too_late' | 'unknown';
39
+ }
40
+ export interface ApplicationResult {
41
+ outcome: 'applied' | 'stale' | 'rejected';
42
+ applicationRevision: number;
43
+ code: 'APPLIED' | 'STALE_EPISODE' | 'OWNER_MISMATCH' | 'ALREADY_CLOSED' | 'INVALID_RESPONSE';
44
+ }
45
+ export interface ConsumerResponse {
46
+ responseId: string;
47
+ deliveryRef: string;
48
+ responseType: string;
49
+ schemaVersion: number;
50
+ data: {
51
+ [key: string]: Json;
52
+ };
53
+ createdAt: string;
54
+ state: 'target_staged' | 'source_recorded' | 'application_applied';
55
+ applicationResult?: ApplicationResult;
56
+ }
57
+ export interface ManagedReceipt {
58
+ eventId: string;
59
+ sourceState: 'captured' | 'empty_audience' | 'rejected' | 'unknown';
60
+ sourceCursor: number;
61
+ scope: Scope;
62
+ routes: RouteReceipt[];
63
+ responses: ConsumerResponse[];
64
+ }
65
+ export interface WithdrawResult {
66
+ operationId: string;
67
+ eventId: string;
68
+ sourceApplied: boolean;
69
+ routes: Array<{
70
+ routeRef: string;
71
+ disposition: 'prevented' | 'too_late' | 'pending' | 'unknown';
72
+ }>;
73
+ }
74
+ export interface ScopeAdvance {
75
+ operationId: string;
76
+ scopeId: string;
77
+ expectedRevision: number;
78
+ nextRevision: number;
79
+ state: 'active' | 'paused' | 'closed';
80
+ }
81
+ export interface ManagedSource {
82
+ publishManaged(event: Event, options: ManagedOptions): Promise<ManagedReceipt>;
83
+ getManagedReceipt(eventId: string): Promise<ManagedReceipt>;
84
+ watchManagedUpdates(after: number, limit: number): Promise<{
85
+ cursor: number;
86
+ resyncRequired: boolean;
87
+ updates: ManagedReceipt[];
88
+ }>;
89
+ withdraw(operationId: string, eventId: string, reason: string): Promise<WithdrawResult>;
90
+ advanceScope(input: ScopeAdvance): Promise<{
91
+ revision: number;
92
+ state: 'active' | 'paused' | 'closed';
93
+ pendingRouteControls: number;
94
+ }>;
95
+ confirmApplied(operationId: string, responseId: string, result: ApplicationResult): Promise<void>;
96
+ dispose(): void;
97
+ }
98
+ export interface GateResult {
99
+ decision: 'allow' | 'defer' | 'drop';
100
+ reasonCode: 'CURRENT' | 'BUSY' | 'SOURCE_UNAVAILABLE' | 'GUARD_UNAVAILABLE' | 'STALE_REQUEST' | 'CANCELLED' | 'WRONG_OWNER' | 'EXPIRED';
101
+ guardEpoch: number;
102
+ validUntil?: string;
103
+ }
104
+ export interface ConsumerContext {
105
+ readonly deliveryRef: string;
106
+ readonly bindingEpoch: number;
107
+ readonly profileId: string;
108
+ }
109
+ export interface ConsumerRegistration {
110
+ profileId: string;
111
+ eventManifestDigest: string;
112
+ responseManifestDigest: string;
113
+ guardImplementationId: string;
114
+ timeoutMs: number;
115
+ }
116
+ export interface ConsumerAPI {
117
+ registerConsumer(profile: ConsumerRegistration, assess: (event: Event, context: ConsumerContext, signal: AbortSignal) => Promise<GateResult>): () => void;
118
+ respond(input: {
119
+ operationId: string;
120
+ deliveryRef: string;
121
+ responseType: string;
122
+ schemaVersion: number;
123
+ data: {
124
+ [key: string]: Json;
125
+ };
126
+ }): Promise<{
127
+ responseId: string;
128
+ state: 'target_staged' | 'source_recorded' | 'application_applied';
129
+ applicationResult?: ApplicationResult;
130
+ }>;
131
+ }
132
+ export interface SetupPlan {
133
+ sourceRef: string;
134
+ channelId: string;
135
+ consumerProfileId: string;
136
+ audienceIntent: 'single-current-owner';
137
+ requestedMode: 'display' | 'resume';
138
+ grant: {
139
+ maxClaims: number;
140
+ ttlMs: number;
141
+ eventTypes: string[];
142
+ };
143
+ }
144
+ export interface OwnerAPI {
145
+ prepareSetup(plan: SetupPlan): Promise<{
146
+ operationId: string;
147
+ planDigest: string;
148
+ ownerPrompt: string;
149
+ }>;
150
+ /** consentReceiptRef comes from trusted owner UI, never from an LLM boolean. */
151
+ commitSetup(input: {
152
+ operationId: string;
153
+ planDigest: string;
154
+ consentReceiptRef: string;
155
+ }): Promise<{
156
+ state: 'complete' | 'partial';
157
+ audienceRef?: string;
158
+ receipts: Json[];
159
+ }>;
160
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Result card (design 6.6): deterministic template built from verified structured facts.
3
+ * Unknown fields are explicitly unknown; a zero exit code does not mean tests/business passed (I14).
4
+ */
5
+ import type { WatchSpec } from '../contracts/interfaces.js';
6
+ import type { WatchSnapshot } from '../storage/store.js';
7
+ export interface ResultCard {
8
+ schemaVersion: 1;
9
+ resultId: string;
10
+ watchId: string;
11
+ generation: number;
12
+ target: WatchSpec['target'];
13
+ executorState: string;
14
+ checks: Array<{
15
+ checkId: string;
16
+ outcome: string;
17
+ artifactDigest: string | null;
18
+ }>;
19
+ artifactIds: string[];
20
+ businessAcceptance: string;
21
+ health: string;
22
+ observedAt: string;
23
+ summary: string;
24
+ /** display-only declaration: local display copy, not a relay delivery fact */
25
+ deliveryNote?: string;
26
+ }
27
+ export declare function buildResultCard(input: {
28
+ resultId: string;
29
+ spec: WatchSpec;
30
+ snapshot: WatchSnapshot;
31
+ requiredCheckIds: readonly string[];
32
+ health: string;
33
+ observedAtMs: number;
34
+ deliveryNote?: string;
35
+ }): ResultCard;
36
+ export interface GroupResultCard {
37
+ schemaVersion: 1;
38
+ resultId: string;
39
+ watchId: string;
40
+ generation: number;
41
+ target: WatchSpec['target'];
42
+ kind: 'group-summary';
43
+ readyWhen: string;
44
+ members: Array<{
45
+ watchId: string;
46
+ taskState: string;
47
+ lifecycle: string;
48
+ }>;
49
+ overall: 'all-succeeded' | 'all-terminal-with-failures' | 'partial';
50
+ health: string;
51
+ observedAt: string;
52
+ summary: string;
53
+ deliveryNote?: string;
54
+ }
55
+ /** V3 group result card: member projection summary; no member-level acceptance judgment (each member's own card is responsible). */
56
+ export declare function buildGroupResultCard(input: {
57
+ resultId: string;
58
+ spec: WatchSpec;
59
+ members: Array<{
60
+ watchId: string;
61
+ taskState: string;
62
+ lifecycle: string;
63
+ }>;
64
+ readyWhen: 'all_terminal' | 'all_succeeded';
65
+ health: string;
66
+ observedAtMs: number;
67
+ deliveryNote?: string;
68
+ }): GroupResultCard;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Result card (design 6.6): deterministic template built from verified structured facts.
3
+ * Unknown fields are explicitly unknown; a zero exit code does not mean tests/business passed (I14).
4
+ */
5
+ import { completionLevel } from './hard-rules.js';
6
+ export function buildResultCard(input) {
7
+ const { resultId, spec, snapshot, requiredCheckIds, health, observedAtMs, deliveryNote } = input;
8
+ const completion = completionLevel(spec, snapshot, requiredCheckIds);
9
+ const summaryLines = [];
10
+ switch (completion.overall) {
11
+ case 'succeeded-evidence':
12
+ summaryLines.push('Succeeded; required checks passed and required artifacts present (verified tier).');
13
+ break;
14
+ case 'failed':
15
+ summaryLines.push(`Failed (exitCode=${snapshot.exitCode ?? 'unknown'}); root cause may still be unknown.`);
16
+ break;
17
+ case 'cancelled':
18
+ summaryLines.push('Execution was cancelled.');
19
+ break;
20
+ case 'incomplete-evidence':
21
+ summaryLines.push('Reported terminal but completion evidence is incomplete (missing checks or artifacts).');
22
+ break;
23
+ case 'in-progress':
24
+ summaryLines.push(`Still running (state=${snapshot.taskState}).`);
25
+ break;
26
+ }
27
+ if (completion.businessAcceptance === 'pending_host') {
28
+ summaryLines.push('Business acceptance pending host (pending_host).');
29
+ }
30
+ const card = {
31
+ schemaVersion: 1,
32
+ resultId,
33
+ watchId: spec.watchId,
34
+ generation: spec.generation,
35
+ target: spec.target,
36
+ executorState: completion.executorState,
37
+ checks: completion.checks,
38
+ artifactIds: snapshot.artifactIds ?? [],
39
+ businessAcceptance: completion.businessAcceptance,
40
+ health,
41
+ observedAt: new Date(observedAtMs).toISOString(),
42
+ summary: summaryLines.join(' ')
43
+ };
44
+ if (deliveryNote) {
45
+ card.deliveryNote = deliveryNote;
46
+ }
47
+ return card;
48
+ }
49
+ /** V3 group result card: member projection summary; no member-level acceptance judgment (each member's own card is responsible). */
50
+ export function buildGroupResultCard(input) {
51
+ const { resultId, spec, members, readyWhen, health, observedAtMs, deliveryNote } = input;
52
+ const succeeded = members.filter(m => m.taskState === 'succeeded').length;
53
+ const terminal = members.filter(m => ['succeeded', 'failed', 'cancelled'].includes(m.taskState)).length;
54
+ const overall = members.length > 0 && succeeded === members.length
55
+ ? 'all-succeeded'
56
+ : members.length > 0 && terminal === members.length
57
+ ? 'all-terminal-with-failures'
58
+ : 'partial';
59
+ const card = {
60
+ schemaVersion: 1,
61
+ resultId,
62
+ watchId: spec.watchId,
63
+ generation: spec.generation,
64
+ target: spec.target,
65
+ kind: 'group-summary',
66
+ readyWhen,
67
+ members,
68
+ overall,
69
+ health,
70
+ observedAt: new Date(observedAtMs).toISOString(),
71
+ summary: `group ${readyWhen}: ${terminal}/${members.length} terminal, ${succeeded}/${members.length} succeeded`
72
+ };
73
+ if (deliveryNote)
74
+ card.deliveryNote = deliveryNote;
75
+ return card;
76
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * WatchEngine: inspection pipeline (deterministic V1 subset of the design 5.2 processing order).
3
+ * 1) validate active/validity -> 2) read events/state (T2 ingest) -> 3) hard rules
4
+ * -> 4) episode supersede + result card (durable outbox + local display copy).
5
+ * The Jev semantic layer is wired in at V2 (JudgePort is ready); V1 has deterministic facts only.
6
+ */
7
+ import type { Observation, SourceAdapter, Probe, JudgePort, ManagedDeliveryPort } from '../contracts/interfaces.js';
8
+ import type { WatchStore, WatchRow, WatchSnapshot } from '../storage/store.js';
9
+ import type { Clock } from '../util/clock.js';
10
+ import { type SemanticThresholds } from './semantic.js';
11
+ export interface WatchEngineOptions {
12
+ clock: Clock;
13
+ store: WatchStore;
14
+ adapters: Map<string, SourceAdapter>;
15
+ requiredCheckIds?: ReadonlyMap<string, readonly string[]>;
16
+ /** V2 hook: shadow/active semantic judgment; may be omitted in V1 */
17
+ judge?: JudgePort;
18
+ /** Note for the local display copy of the result card */
19
+ displayNote?: string;
20
+ /** V2 semantic-layer config (defaults to policy-defaults) */
21
+ semantic?: SemanticEngineConfig;
22
+ /** Data egress consent (required for live Jev; Mock does not export locally, tests may set judgeRequiresConsent=false) */
23
+ semanticConsent?: boolean | (() => boolean);
24
+ /** Whether the judge is egress-type (needs consent); pass false for Mock local judgment */
25
+ judgeRequiresConsent?: boolean;
26
+ /** V1 closed loop: relay managed delivery port (static or live reference; upgraded after mid-session bind) */
27
+ delivery?: ManagedDeliveryPort | (() => ManagedDeliveryPort | undefined);
28
+ /** Notification hook after attention lands in the outbox (for the display layer; does not block the engine) */
29
+ onAttention?: (notice: AttentionNotice) => void;
30
+ /** Judgment-complete notice (display layer; fires after an accepted judgment, must not block the engine) */
31
+ onJudgment?: (notice: JudgmentNotice) => void;
32
+ }
33
+ /** Attention notification published by the engine (passed across layers to the display layer / session toast). */
34
+ export interface AttentionNotice {
35
+ watchId: string;
36
+ ownerSession?: string;
37
+ episodeId: string;
38
+ reasonCode: string;
39
+ summary: string;
40
+ transport: 'relay-managed' | 'local-display' | 'relay-failed';
41
+ /** Failure reason when transport=relay-failed (same source as outbox admission_json.error) */
42
+ relayError?: string;
43
+ /** Deadline until which the envelope is still valid (ms epoch) */
44
+ validUntil: number;
45
+ }
46
+ /** Accepted-judgment-complete notification (display-layer notice; judge review must be visible) */
47
+ export interface JudgmentNotice {
48
+ watchId: string;
49
+ ownerSession?: string;
50
+ objective: string;
51
+ /** shadow = record only; active = may create episode/attention */
52
+ mode: 'shadow' | 'active';
53
+ /** Candidates that hit the threshold (none -> everything normal, no action needed) */
54
+ candidates: Array<{
55
+ reason: string;
56
+ probability: number;
57
+ note: string | null;
58
+ }>;
59
+ probabilities: Record<string, number>;
60
+ }
61
+ export interface SemanticEngineConfig {
62
+ thresholds: SemanticThresholds;
63
+ maxJudgeRequestsPerRootDay: number;
64
+ maxProbesPerInspection: number;
65
+ maxProbesPerEpisode: number;
66
+ maxEvidenceEvents: number;
67
+ stateMaxBytes: number;
68
+ }
69
+ export declare const DEFAULT_SEMANTIC_CONFIG: SemanticEngineConfig;
70
+ /** Default attention envelope validity: real wake latency includes safety-net ticks / long turns,
71
+ * 300s is not enough; also bounds the fast-close line. */
72
+ export declare const DEFAULT_ATTENTION_TTL_MS = 1800000;
73
+ export interface InspectionOutcome {
74
+ inspectionId: string;
75
+ watchId: string;
76
+ ingested: number;
77
+ produced: {
78
+ episodes: string[];
79
+ resultCards: string[];
80
+ };
81
+ health: WatchRow['health'];
82
+ lifecycleAfter: WatchRow['lifecycle'];
83
+ }
84
+ export declare class WatchEngine {
85
+ private readonly clock;
86
+ private readonly store;
87
+ private readonly adapters;
88
+ private readonly requiredCheckIds;
89
+ private readonly judge?;
90
+ private readonly displayNote;
91
+ private readonly semanticConfig;
92
+ private readonly semanticConsentRef;
93
+ private readonly judgeRequiresConsent;
94
+ private readonly deliveryRef;
95
+ private readonly onAttention?;
96
+ private readonly onJudgment?;
97
+ /** Single-flight per Watch (design 5.6) */
98
+ private readonly semanticInFlight;
99
+ /** After 401/403, egress is disabled within this process (design 5.6) */
100
+ private judgeDisabled;
101
+ constructor(options: WatchEngineOptions);
102
+ /** Consent is read live: the user may grant or revoke it mid-session (/watcher jev consent). */
103
+ private get semanticConsent();
104
+ private get delivery();
105
+ /** Run due inspections: returns the list of executed inspectionIds. */
106
+ runDue(limit?: number): Promise<string[]>;
107
+ inspectWatch(watchId: string, signal?: AbortSignal): Promise<InspectionOutcome | null>;
108
+ /**
109
+ * V2 semantic inspection (design 5.2 steps 3-5 / 5.5 / 5.6).
110
+ * Network calls do not hold a SQL write transaction; single-flight per Watch; late results are discarded after version check.
111
+ */
112
+ private runSemanticPass;
113
+ /** V3: group (design 3.x watch-group) -- reads committed child Watch state. */
114
+ private inspectGroup;
115
+ /** V3: explicit follow-up obligation (pi-obligation-v1 semantics; pure time reminder = obligation without dependencies). */
116
+ private inspectObligation;
117
+ /** Unified attention publishing for hard facts / semantic candidates: truth lands in the outbox first, then publishes via relay managed delivery (I6). */
118
+ private publishEpisodeAttention;
119
+ /**
120
+ * Publishes an attention through the relay managed path; only advances admission, never the
121
+ * frozen truth (I06). The scope used is recorded in admission_json so a retry re-sends the
122
+ * identical (event, options) pair. Never throws: returns status for loud failure handling.
123
+ */
124
+ private publishAttention;
125
+ /** Records that an attention could not be published yet (scope control unconfirmed). */
126
+ private deferAttention;
127
+ /**
128
+ * Returns the watch's relay scope, creating it on first use (expectedRevision=0 ->
129
+ * nextRevision=1, design 9.6). Unconfirmed control ops for the watch are drained first;
130
+ * if any remain open, or the scope is not active, publishing is blocked (design 7.7).
131
+ * scopeId = scope-<watchId>-g<generation>-e<bindingEpoch>: a generation or owner-binding
132
+ * change yields a new scope, and the old one is never reused.
133
+ */
134
+ private ensureRelayScope;
135
+ /**
136
+ * Drains the durable control outbox (design 7.3 T6 / 7.7): scope advances FIFO per watch,
137
+ * then pending event withdraws. Each op is retried under its original operationId; the
138
+ * relay dedupes by operationId + request digest. Deterministic relay refusals are recorded
139
+ * as rejected (no retry); anything else stays unknown and is retried on the next sweep.
140
+ */
141
+ drainControl(watchId?: string): Promise<void>;
142
+ /**
143
+ * Delivery health: for attentions that are unknown (publish failed)
144
+ * or deferred (scope control unconfirmed) while the episode is still open, each sweep
145
+ * (a) retries once if the envelope is still valid, re-sending the same (event, options);
146
+ * (b) otherwise only reports loudly (the host must inspect).
147
+ * Withdrawn events are never re-published; an event whose recorded scope has since
148
+ * advanced is fenced and marked rejected instead of being re-sent under a new scope.
149
+ */
150
+ private checkDeliveryHealth;
151
+ /** Terminal-state watch auto-close: supersede open episodes -> superseded, lifecycle -> closed (does not touch controlRevision CAS semantics). */
152
+ private autoCloseTerminal;
153
+ private degrade;
154
+ /**
155
+ * Delivery guard: there is a wake the host never confirmed -- episode still open and its
156
+ * attention admission is in {unknown, pending, publishing} (publish unconfirmed) or =source-staged
157
+ * and the envelope has not expired (relay delivery in flight) -> returns true, blocking ttlElapsed auto-close.
158
+ */
159
+ private hasUndeliveredWake;
160
+ /** slot = (watchId, generation, kind, checkpointId); at most one active episode per slot (design 3.3). */
161
+ private openEpisode;
162
+ private ensureMonitorEpisode;
163
+ /** Result card: truth = durable outbox (I6: event bytes/ID/expiry frozen); the display copy is persisted separately (I16). */
164
+ private commitResultCard;
165
+ /** probes delegation (trusted candidates, design 4.4) */
166
+ listProbes(watchId: string): Promise<readonly Probe[]>;
167
+ executeProbe(watchId: string, probeId: string, signal?: AbortSignal): Promise<Observation[]>;
168
+ }
169
+ /**
170
+ * Projects one identity-checked observation onto the snapshot. Shared by ingest and probe
171
+ * ingestion so both paths derive the same state from the same evidence. activityAtMs is set
172
+ * when the observation counts as task activity (status/heartbeat/log_delta) for the silence
173
+ * rule; validation/artifact observations do not.
174
+ */
175
+ export declare function applyObservationToSnapshot(snapshot: WatchSnapshot, obs: Observation, now: number): {
176
+ snapshot: WatchSnapshot;
177
+ activityAtMs: number | null;
178
+ };