@femtomc/mu-server 26.2.90 → 26.2.91

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.
@@ -1,266 +0,0 @@
1
- import { reconcileRunQueue, runSnapshotFromQueueSnapshot, } from "./run_queue.js";
2
- const DEFAULT_RUN_MAX_STEPS = 20;
3
- const MAX_RECONCILE_TURNS = 256;
4
- function toPositiveInt(value, fallback) {
5
- if (typeof value === "number" && Number.isFinite(value)) {
6
- return Math.max(1, Math.trunc(value));
7
- }
8
- if (typeof value === "string" && /^\d+$/.test(value.trim())) {
9
- return Math.max(1, Number.parseInt(value, 10));
10
- }
11
- return Math.max(1, Math.trunc(fallback));
12
- }
13
- function normalizeIssueId(value) {
14
- if (!value) {
15
- return null;
16
- }
17
- const trimmed = value.trim();
18
- if (!/^mu-[a-z0-9][a-z0-9-]*$/i.test(trimmed)) {
19
- return null;
20
- }
21
- return trimmed.toLowerCase();
22
- }
23
- /**
24
- * Queue/reconcile adapter for control-plane run lifecycle operations.
25
- *
26
- * Keeps inter-root queue execution concerns out of `control_plane.ts` while preserving
27
- * queue-first semantics:
28
- * - enqueue intents durably
29
- * - reconcile/claim/launch deterministically
30
- * - mirror runtime events back into durable queue state
31
- */
32
- export class ControlPlaneRunQueueCoordinator {
33
- #runQueue;
34
- #interRootQueuePolicy;
35
- #getRunSupervisor;
36
- #defaultRunMaxSteps;
37
- #queuedCommandById = new Map();
38
- #queueReconcileCounter = 0;
39
- #queueReconcileTail = Promise.resolve();
40
- constructor(opts) {
41
- this.#runQueue = opts.runQueue;
42
- this.#interRootQueuePolicy = opts.interRootQueuePolicy;
43
- this.#getRunSupervisor = opts.getRunSupervisor;
44
- this.#defaultRunMaxSteps = toPositiveInt(opts.defaultRunMaxSteps, DEFAULT_RUN_MAX_STEPS);
45
- }
46
- runtimeSnapshotsByJobId() {
47
- const map = new Map();
48
- for (const run of this.#getRunSupervisor()?.list({ limit: 500 }) ?? []) {
49
- map.set(run.job_id, run);
50
- }
51
- return map;
52
- }
53
- async #launchQueueEntry(row, opPrefix) {
54
- const runSupervisor = this.#getRunSupervisor();
55
- if (!runSupervisor) {
56
- throw new Error("run_supervisor_unavailable");
57
- }
58
- const command = row.command_id ? (this.#queuedCommandById.get(row.command_id) ?? null) : null;
59
- try {
60
- const launched = row.mode === "run_start"
61
- ? await runSupervisor.launchStart({
62
- prompt: row.prompt ?? "",
63
- maxSteps: row.max_steps,
64
- command,
65
- commandId: row.command_id,
66
- source: row.source,
67
- })
68
- : await runSupervisor.launchResume({
69
- rootIssueId: row.root_issue_id ?? "",
70
- maxSteps: row.max_steps,
71
- command,
72
- commandId: row.command_id,
73
- source: row.source,
74
- });
75
- if (command?.command_id) {
76
- this.#queuedCommandById.delete(command.command_id);
77
- }
78
- const latestSnapshot = runSupervisor.get(launched.job_id) ?? launched;
79
- await this.#runQueue.bindRunSnapshot({
80
- queueId: row.queue_id,
81
- run: latestSnapshot,
82
- operationId: `${opPrefix}:bind:${row.queue_id}:${launched.job_id}`,
83
- });
84
- }
85
- catch {
86
- if (command?.command_id) {
87
- this.#queuedCommandById.delete(command.command_id);
88
- }
89
- await this.#runQueue.transition({
90
- queueId: row.queue_id,
91
- toState: "failed",
92
- operationId: `${opPrefix}:failed:${row.queue_id}`,
93
- });
94
- }
95
- }
96
- async #reconcileQueuedRunsNow(reason) {
97
- const runSupervisor = this.#getRunSupervisor();
98
- if (!runSupervisor) {
99
- return;
100
- }
101
- for (let turn = 0; turn < MAX_RECONCILE_TURNS; turn += 1) {
102
- const rows = await this.#runQueue.list({ limit: 500 });
103
- const plan = reconcileRunQueue(rows, this.#interRootQueuePolicy);
104
- if (plan.activate_queue_ids.length === 0 && plan.launch_queue_ids.length === 0) {
105
- return;
106
- }
107
- if (plan.activate_queue_ids.length > 0) {
108
- for (const queueId of plan.activate_queue_ids) {
109
- await this.#runQueue.claim({
110
- queueId,
111
- operationId: `reconcile:${reason}:activate:${queueId}:${turn}`,
112
- });
113
- }
114
- continue;
115
- }
116
- for (const queueId of plan.launch_queue_ids) {
117
- const row = await this.#runQueue.get(queueId);
118
- if (!row || row.state !== "active") {
119
- continue;
120
- }
121
- if (row.job_id) {
122
- const existing = runSupervisor.get(row.job_id);
123
- if (existing) {
124
- await this.#runQueue.applyRunSnapshot({
125
- queueId: row.queue_id,
126
- run: existing,
127
- operationId: `reconcile:${reason}:existing:${row.queue_id}:${existing.updated_at_ms}`,
128
- });
129
- continue;
130
- }
131
- }
132
- await this.#launchQueueEntry(row, `reconcile:${reason}:launch:${turn}`);
133
- }
134
- }
135
- }
136
- async scheduleReconcile(reason) {
137
- const token = `${++this.#queueReconcileCounter}:${reason}`;
138
- this.#queueReconcileTail = this.#queueReconcileTail.then(async () => await this.#reconcileQueuedRunsNow(token), async () => await this.#reconcileQueuedRunsNow(token));
139
- return await this.#queueReconcileTail;
140
- }
141
- async launchQueuedRun(launchOpts) {
142
- const runSupervisor = this.#getRunSupervisor();
143
- if (!runSupervisor) {
144
- throw new Error("run_supervisor_unavailable");
145
- }
146
- const dedupeKey = launchOpts.dedupeKey.trim();
147
- if (!dedupeKey) {
148
- throw new Error("run_queue_dedupe_key_required");
149
- }
150
- const maxSteps = toPositiveInt(launchOpts.maxSteps, this.#defaultRunMaxSteps);
151
- const queued = await this.#runQueue.enqueue({
152
- mode: launchOpts.mode,
153
- prompt: launchOpts.mode === "run_start" ? (launchOpts.prompt ?? null) : null,
154
- rootIssueId: launchOpts.mode === "run_resume" ? (launchOpts.rootIssueId ?? null) : null,
155
- maxSteps,
156
- commandId: launchOpts.command?.command_id ?? null,
157
- source: launchOpts.source,
158
- dedupeKey,
159
- operationId: `enqueue:${dedupeKey}`,
160
- });
161
- if (launchOpts.command?.command_id) {
162
- this.#queuedCommandById.set(launchOpts.command.command_id, launchOpts.command);
163
- }
164
- await this.scheduleReconcile(`enqueue:${queued.queue_id}`);
165
- const refreshed = (await this.#runQueue.get(queued.queue_id)) ?? queued;
166
- const runtime = refreshed.job_id ? (runSupervisor.get(refreshed.job_id) ?? null) : null;
167
- return runSnapshotFromQueueSnapshot(refreshed, runtime);
168
- }
169
- async launchQueuedRunFromCommand(record) {
170
- if (record.target_type === "run start") {
171
- const prompt = record.command_args.join(" ").trim();
172
- if (!prompt) {
173
- throw new Error("run_start_prompt_required");
174
- }
175
- return await this.launchQueuedRun({
176
- mode: "run_start",
177
- prompt,
178
- source: "command",
179
- command: record,
180
- dedupeKey: `command:${record.command_id}`,
181
- });
182
- }
183
- if (record.target_type === "run resume") {
184
- const fallbackRoot = normalizeIssueId(record.target_id);
185
- const explicitRoot = normalizeIssueId(record.command_args[0] ?? "") ?? fallbackRoot;
186
- if (!explicitRoot) {
187
- throw new Error("run_resume_invalid_root_issue_id");
188
- }
189
- const maxSteps = toPositiveInt(record.command_args[1], this.#defaultRunMaxSteps);
190
- return await this.launchQueuedRun({
191
- mode: "run_resume",
192
- rootIssueId: explicitRoot,
193
- maxSteps,
194
- source: "command",
195
- command: record,
196
- dedupeKey: `command:${record.command_id}`,
197
- });
198
- }
199
- throw new Error("run_queue_invalid_command_target");
200
- }
201
- async interruptQueuedRun(opts) {
202
- const runSupervisor = this.#getRunSupervisor();
203
- const result = runSupervisor?.interrupt(opts) ?? { ok: false, reason: "not_found", run: null };
204
- if (result.run) {
205
- await this.#runQueue
206
- .applyRunSnapshot({
207
- run: result.run,
208
- operationId: `interrupt-sync:${result.run.job_id}:${result.run.updated_at_ms}`,
209
- createIfMissing: true,
210
- })
211
- .catch(() => {
212
- // Best effort only.
213
- });
214
- }
215
- if (result.ok) {
216
- return result;
217
- }
218
- const target = opts.jobId?.trim() || opts.rootIssueId?.trim() || "";
219
- if (!target) {
220
- return result;
221
- }
222
- const queued = await this.#runQueue.get(target);
223
- if (!queued) {
224
- return result;
225
- }
226
- if (queued.state === "queued" ||
227
- queued.state === "active" ||
228
- queued.state === "waiting_review" ||
229
- queued.state === "refining") {
230
- const cancelled = await this.#runQueue.transition({
231
- queueId: queued.queue_id,
232
- toState: "cancelled",
233
- operationId: `interrupt-cancel:${target}`,
234
- });
235
- await this.scheduleReconcile(`interrupt:${queued.queue_id}`);
236
- return {
237
- ok: true,
238
- reason: null,
239
- run: runSnapshotFromQueueSnapshot(cancelled),
240
- };
241
- }
242
- return {
243
- ok: false,
244
- reason: "not_running",
245
- run: runSnapshotFromQueueSnapshot(queued),
246
- };
247
- }
248
- async onRunEvent(event) {
249
- await this.#runQueue
250
- .applyRunSnapshot({
251
- run: event.run,
252
- operationId: `run-event:${event.seq}:${event.kind}`,
253
- createIfMissing: false,
254
- })
255
- .catch(() => {
256
- // Best effort queue reconciliation from runtime events.
257
- return null;
258
- });
259
- if (event.kind === "run_completed" || event.kind === "run_failed" || event.kind === "run_cancelled") {
260
- await this.scheduleReconcile(`terminal:${event.kind}:${event.run.job_id}`);
261
- }
262
- }
263
- stop() {
264
- this.#queuedCommandById.clear();
265
- }
266
- }
@@ -1,44 +0,0 @@
1
- export type OrchestrationQueueState = "queued" | "active" | "waiting_review" | "refining" | "done" | "failed" | "cancelled";
2
- /**
3
- * Inter-root scheduler policy knob for durable queue drain.
4
- * - sequential: exactly one active root (`max_active_roots=1`)
5
- * - parallel: bounded fanout (`max_active_roots>=1`)
6
- */
7
- export type InterRootQueuePolicy = {
8
- mode: "sequential";
9
- max_active_roots: 1;
10
- } | {
11
- mode: "parallel";
12
- max_active_roots: number;
13
- };
14
- export declare const DEFAULT_INTER_ROOT_QUEUE_POLICY: InterRootQueuePolicy;
15
- export declare function normalizeInterRootQueuePolicy(policy: InterRootQueuePolicy | null | undefined): InterRootQueuePolicy;
16
- /**
17
- * Allowed queue transitions. Queue tests should enforce this table exactly.
18
- */
19
- export declare const ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS: Record<OrchestrationQueueState, readonly OrchestrationQueueState[]>;
20
- export declare const ORCHESTRATION_QUEUE_INVARIANTS: readonly ["ORCH-QUEUE-001: Queue writes are durable before acknowledging enqueue/start/resume requests.", "ORCH-QUEUE-002: Queue dispatch must claim exactly one active item at a time per root slot.", "ORCH-QUEUE-003: Terminal states (done|failed|cancelled) are immutable.", "ORCH-QUEUE-004: Review path is active -> waiting_review -> (done | refining).", "ORCH-QUEUE-005: Refinement re-enters execution only via refining -> queued.", "ORCH-QUEUE-006: sequential policy permits <=1 active root; parallel permits <=max_active_roots active roots."];
21
- export type InterRootQueueSnapshot = {
22
- queue_id: string;
23
- root_issue_id: string | null;
24
- state: OrchestrationQueueState;
25
- job_id: string | null;
26
- created_at_ms: number;
27
- };
28
- export type InterRootQueueReconcilePlan = {
29
- policy: InterRootQueuePolicy;
30
- max_active_roots: number;
31
- active_root_count: number;
32
- available_root_slots: number;
33
- activate_queue_ids: string[];
34
- launch_queue_ids: string[];
35
- };
36
- export declare const INTER_ROOT_QUEUE_RECONCILE_INVARIANTS: readonly ["ORCH-INTER-ROOT-RECON-001: one queue snapshot + policy yields one deterministic activation/launch plan.", "ORCH-INTER-ROOT-RECON-002: activation order is FIFO (`created_at_ms`, then `queue_id`) with per-root slot dedupe.", "ORCH-INTER-ROOT-RECON-003: sequential policy admits <=1 occupied root; parallel admits <=max_active_roots roots.", "ORCH-INTER-ROOT-RECON-004: launch candidates are active rows without bound job ids, one launch per root slot."];
37
- /**
38
- * Deterministic inter-root queue reconcile primitive.
39
- *
40
- * Computes queue activation/launch intentions from durable queue state and policy. The caller is
41
- * responsible for performing side effects (claim, launch, bind, transition) and reconciling again
42
- * against refreshed queue/runtime snapshots.
43
- */
44
- export declare function reconcileInterRootQueue<Row extends InterRootQueueSnapshot>(rows: readonly Row[], policy: InterRootQueuePolicy): InterRootQueueReconcilePlan;
@@ -1,111 +0,0 @@
1
- export const DEFAULT_INTER_ROOT_QUEUE_POLICY = {
2
- mode: "sequential",
3
- max_active_roots: 1,
4
- };
5
- export function normalizeInterRootQueuePolicy(policy) {
6
- if (!policy) {
7
- return DEFAULT_INTER_ROOT_QUEUE_POLICY;
8
- }
9
- if (policy.mode === "parallel") {
10
- return {
11
- mode: "parallel",
12
- max_active_roots: Math.max(1, Math.trunc(policy.max_active_roots)),
13
- };
14
- }
15
- return DEFAULT_INTER_ROOT_QUEUE_POLICY;
16
- }
17
- /**
18
- * Allowed queue transitions. Queue tests should enforce this table exactly.
19
- */
20
- export const ORCHESTRATION_QUEUE_ALLOWED_TRANSITIONS = {
21
- queued: ["active", "cancelled"],
22
- active: ["waiting_review", "done", "failed", "cancelled"],
23
- waiting_review: ["refining", "done", "failed", "cancelled"],
24
- refining: ["queued", "failed", "cancelled"],
25
- done: [],
26
- failed: [],
27
- cancelled: [],
28
- };
29
- export const ORCHESTRATION_QUEUE_INVARIANTS = [
30
- "ORCH-QUEUE-001: Queue writes are durable before acknowledging enqueue/start/resume requests.",
31
- "ORCH-QUEUE-002: Queue dispatch must claim exactly one active item at a time per root slot.",
32
- "ORCH-QUEUE-003: Terminal states (done|failed|cancelled) are immutable.",
33
- "ORCH-QUEUE-004: Review path is active -> waiting_review -> (done | refining).",
34
- "ORCH-QUEUE-005: Refinement re-enters execution only via refining -> queued.",
35
- "ORCH-QUEUE-006: sequential policy permits <=1 active root; parallel permits <=max_active_roots active roots.",
36
- ];
37
- const INTER_ROOT_OCCUPIED_STATES = new Set(["active", "waiting_review", "refining"]);
38
- export const INTER_ROOT_QUEUE_RECONCILE_INVARIANTS = [
39
- "ORCH-INTER-ROOT-RECON-001: one queue snapshot + policy yields one deterministic activation/launch plan.",
40
- "ORCH-INTER-ROOT-RECON-002: activation order is FIFO (`created_at_ms`, then `queue_id`) with per-root slot dedupe.",
41
- "ORCH-INTER-ROOT-RECON-003: sequential policy admits <=1 occupied root; parallel admits <=max_active_roots roots.",
42
- "ORCH-INTER-ROOT-RECON-004: launch candidates are active rows without bound job ids, one launch per root slot.",
43
- ];
44
- function stableCompare(a, b) {
45
- if (a.created_at_ms !== b.created_at_ms) {
46
- return a.created_at_ms - b.created_at_ms;
47
- }
48
- return a.queue_id.localeCompare(b.queue_id);
49
- }
50
- function normalizeMaxActiveRoots(policy) {
51
- if (policy.mode === "parallel") {
52
- return Math.max(1, Math.trunc(policy.max_active_roots));
53
- }
54
- return 1;
55
- }
56
- function queueRootSlotKey(row) {
57
- return row.root_issue_id ? `root:${row.root_issue_id}` : `queue:${row.queue_id}`;
58
- }
59
- /**
60
- * Deterministic inter-root queue reconcile primitive.
61
- *
62
- * Computes queue activation/launch intentions from durable queue state and policy. The caller is
63
- * responsible for performing side effects (claim, launch, bind, transition) and reconciling again
64
- * against refreshed queue/runtime snapshots.
65
- */
66
- export function reconcileInterRootQueue(rows, policy) {
67
- const sorted = [...rows].sort(stableCompare);
68
- const maxActiveRoots = normalizeMaxActiveRoots(policy);
69
- const occupiedRoots = new Set();
70
- const launchRoots = new Set();
71
- const launchQueueIds = [];
72
- for (const row of sorted) {
73
- if (!INTER_ROOT_OCCUPIED_STATES.has(row.state)) {
74
- continue;
75
- }
76
- const slotKey = queueRootSlotKey(row);
77
- occupiedRoots.add(slotKey);
78
- if (row.state !== "active" || row.job_id != null || launchRoots.has(slotKey)) {
79
- continue;
80
- }
81
- launchRoots.add(slotKey);
82
- launchQueueIds.push(row.queue_id);
83
- }
84
- const availableRootSlots = Math.max(0, maxActiveRoots - occupiedRoots.size);
85
- const claimedRoots = new Set();
86
- const activateQueueIds = [];
87
- if (availableRootSlots > 0) {
88
- for (const row of sorted) {
89
- if (row.state !== "queued") {
90
- continue;
91
- }
92
- const slotKey = queueRootSlotKey(row);
93
- if (occupiedRoots.has(slotKey) || claimedRoots.has(slotKey)) {
94
- continue;
95
- }
96
- claimedRoots.add(slotKey);
97
- activateQueueIds.push(row.queue_id);
98
- if (activateQueueIds.length >= availableRootSlots) {
99
- break;
100
- }
101
- }
102
- }
103
- return {
104
- policy,
105
- max_active_roots: maxActiveRoots,
106
- active_root_count: occupiedRoots.size,
107
- available_root_slots: availableRootSlots,
108
- activate_queue_ids: activateQueueIds,
109
- launch_queue_ids: launchQueueIds,
110
- };
111
- }
@@ -1,95 +0,0 @@
1
- import type { JsonlStore } from "@femtomc/mu-core";
2
- import { type InterRootQueuePolicy, type InterRootQueueReconcilePlan, type OrchestrationQueueState } from "./orchestration_queue.js";
3
- import type { ControlPlaneRunMode, ControlPlaneRunSnapshot, ControlPlaneRunStatus } from "./run_supervisor.js";
4
- export type DurableRunQueueState = OrchestrationQueueState;
5
- export type DurableRunQueueSnapshot = {
6
- v: 1;
7
- queue_id: string;
8
- dedupe_key: string;
9
- mode: ControlPlaneRunMode;
10
- state: DurableRunQueueState;
11
- prompt: string | null;
12
- root_issue_id: string | null;
13
- max_steps: number;
14
- command_id: string | null;
15
- source: "command" | "api";
16
- job_id: string | null;
17
- started_at_ms: number | null;
18
- updated_at_ms: number;
19
- finished_at_ms: number | null;
20
- exit_code: number | null;
21
- pid: number | null;
22
- last_progress: string | null;
23
- created_at_ms: number;
24
- revision: number;
25
- applied_operation_ids: string[];
26
- };
27
- export type DurableRunQueueOpts = {
28
- repoRoot: string;
29
- nowMs?: () => number;
30
- store?: JsonlStore<unknown>;
31
- maxOperationIds?: number;
32
- };
33
- export type DurableRunQueueEnqueueOpts = {
34
- mode: ControlPlaneRunMode;
35
- prompt: string | null;
36
- rootIssueId: string | null;
37
- maxSteps?: number;
38
- commandId?: string | null;
39
- source: "command" | "api";
40
- dedupeKey: string;
41
- operationId?: string | null;
42
- nowMs?: number;
43
- };
44
- export type DurableRunQueueTransitionOpts = {
45
- queueId: string;
46
- toState: DurableRunQueueState;
47
- operationId?: string | null;
48
- nowMs?: number;
49
- };
50
- export type DurableRunQueueClaimOpts = {
51
- queueId?: string | null;
52
- operationId?: string | null;
53
- nowMs?: number;
54
- };
55
- export declare function runStatusFromQueueState(state: DurableRunQueueState): ControlPlaneRunStatus;
56
- export declare function queueStatesForRunStatusFilter(status: string | null | undefined): DurableRunQueueState[] | null;
57
- export type RunQueueReconcilePlan = InterRootQueueReconcilePlan;
58
- export declare const RUN_QUEUE_RECONCILE_INVARIANTS: readonly ["ORCH-INTER-ROOT-RECON-001: one queue snapshot + policy yields one deterministic activation/launch plan.", "ORCH-INTER-ROOT-RECON-002: activation order is FIFO (`created_at_ms`, then `queue_id`) with per-root slot dedupe.", "ORCH-INTER-ROOT-RECON-003: sequential policy admits <=1 occupied root; parallel admits <=max_active_roots roots.", "ORCH-INTER-ROOT-RECON-004: launch candidates are active rows without bound job ids, one launch per root slot."];
59
- /**
60
- * Server adapter wrapper around the orchestrator-owned inter-root planner.
61
- */
62
- export declare function reconcileRunQueue(rows: readonly DurableRunQueueSnapshot[], policy: InterRootQueuePolicy): RunQueueReconcilePlan;
63
- export declare function runQueuePath(repoRoot: string): string;
64
- export declare function runSnapshotFromQueueSnapshot(queue: DurableRunQueueSnapshot, runtime?: ControlPlaneRunSnapshot | null): ControlPlaneRunSnapshot;
65
- export declare class DurableRunQueue {
66
- #private;
67
- constructor(opts: DurableRunQueueOpts);
68
- enqueue(opts: DurableRunQueueEnqueueOpts): Promise<DurableRunQueueSnapshot>;
69
- claim(opts?: DurableRunQueueClaimOpts): Promise<DurableRunQueueSnapshot | null>;
70
- activate(opts: DurableRunQueueClaimOpts): Promise<DurableRunQueueSnapshot | null>;
71
- transition(opts: DurableRunQueueTransitionOpts): Promise<DurableRunQueueSnapshot>;
72
- bindRunSnapshot(opts: {
73
- queueId: string;
74
- run: ControlPlaneRunSnapshot;
75
- operationId?: string | null;
76
- nowMs?: number;
77
- }): Promise<DurableRunQueueSnapshot>;
78
- applyRunSnapshot(opts: {
79
- run: ControlPlaneRunSnapshot;
80
- queueId?: string | null;
81
- operationId?: string | null;
82
- nowMs?: number;
83
- createIfMissing?: boolean;
84
- }): Promise<DurableRunQueueSnapshot | null>;
85
- get(idOrRoot: string): Promise<DurableRunQueueSnapshot | null>;
86
- list(opts?: {
87
- states?: readonly DurableRunQueueState[];
88
- limit?: number;
89
- }): Promise<DurableRunQueueSnapshot[]>;
90
- listRunSnapshots(opts?: {
91
- status?: string;
92
- limit?: number;
93
- runtimeByJobId?: Map<string, ControlPlaneRunSnapshot>;
94
- }): Promise<ControlPlaneRunSnapshot[]>;
95
- }