@henols/vice-mcp 0.2.2 → 0.2.3

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 (77) hide show
  1. package/README.md +2 -2
  2. package/THIRD-PARTY-NOTICES.md +422 -1
  3. package/anno-bank.ts +171 -0
  4. package/anno-cli.ts +1674 -99
  5. package/anno-enum-gen.ts +416 -30
  6. package/anno-export-asm.ts +1175 -89
  7. package/anno-graphics.ts +338 -0
  8. package/anno-hazard-report.ts +1367 -0
  9. package/anno-import.ts +495 -0
  10. package/anno-join.ts +480 -0
  11. package/anno-provenance-ledger.ts +472 -0
  12. package/anno-register.ts +159 -0
  13. package/anno-store-export.ts +661 -0
  14. package/anno-store.ts +518 -2
  15. package/anno-tools.ts +1169 -16
  16. package/anno-types.ts +275 -2
  17. package/backend-detect.mts +124 -312
  18. package/build.ts +3 -1
  19. package/capture-predicate.ts +597 -0
  20. package/channel-lock.ts +349 -0
  21. package/evid-ingest.ts +217 -0
  22. package/evid-reconcile.ts +316 -0
  23. package/host-tool-client.ts +430 -0
  24. package/incident-record.ts +23 -12
  25. package/install-resources.ts +29 -13
  26. package/memmap-lookup.ts +285 -0
  27. package/package.json +27 -8
  28. package/prg-image.ts +1 -2
  29. package/repo-root.ts +87 -3
  30. package/resources/backend-detect.mjs +98 -236
  31. package/resources/broker-control.mjs +189 -16
  32. package/resources/broker-epoch.mjs +1 -1
  33. package/resources/broker-kill.mjs +8 -2
  34. package/resources/broker-launch.mjs +365 -210
  35. package/resources/broker-state.mjs +64 -18
  36. package/resources/container-guard.mjs +1 -1
  37. package/resources/ghidra-project.mjs +790 -0
  38. package/resources/host-tool.mjs +2561 -0
  39. package/resources/vice-broker.mjs +330 -184
  40. package/resources/vice-launcher.sh +127 -9
  41. package/stock-address.ts +1 -1
  42. package/stock-condition.ts +1 -1
  43. package/stock-connect.ts +9 -5
  44. package/stock-derived.ts +29 -37
  45. package/stock-diagnose.ts +200 -36
  46. package/stock-dispatch.ts +179 -77
  47. package/stock-handler.ts +1 -1
  48. package/stock-paths.ts +18 -14
  49. package/stock-petscii.ts +1 -1
  50. package/stock-protocol.ts +1 -1
  51. package/stock-recycle.ts +83 -2
  52. package/stock-reproducible-run.ts +811 -0
  53. package/stock-run-until.ts +100 -1
  54. package/stock-symbols.ts +4 -4
  55. package/stock-timing.ts +1 -1
  56. package/stop-oracle.ts +167 -0
  57. package/text-capability-probe.ts +660 -0
  58. package/text-connect.ts +157 -0
  59. package/text-protocol.ts +810 -0
  60. package/text-tools.ts +778 -0
  61. package/textmon-backtrace.ts +385 -0
  62. package/textmon-cpuhistory.ts +335 -0
  63. package/textmon-memmap.ts +494 -0
  64. package/textmon-profile.ts +458 -0
  65. package/textmon-registers.ts +748 -0
  66. package/tools-manifest.stock.json +864 -3
  67. package/vice-broker-client.ts +189 -42
  68. package/vice-errors.ts +268 -0
  69. package/vice-proxy.ts +339 -2144
  70. package/vsf-slice.ts +640 -0
  71. package/anno-d64.ts +0 -310
  72. package/capability-registry.ts +0 -390
  73. package/refresh-manifest.ts +0 -124
  74. package/tools-manifest.json +0 -1223
  75. package/vice-probe.ts +0 -278
  76. package/vice-sync.ts +0 -336
  77. package/vice.ts +0 -772
package/anno-types.ts CHANGED
@@ -111,7 +111,7 @@ import { existsSync, lstatSync, readlinkSync, realpathSync } from "node:fs";
111
111
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
112
112
 
113
113
  import { OPCODES } from "./disasm-opcodes.ts";
114
- import { ViceError, type ViceErrorOptions } from "./vice.ts";
114
+ import { ViceError, type ViceErrorOptions } from "./vice-errors.ts";
115
115
 
116
116
  /**
117
117
  * The on-disk schema version every store file carries in `anno_meta`. A
@@ -182,8 +182,158 @@ import { ViceError, type ViceErrorOptions } from "./vice.ts";
182
182
  * exist, and it would put new code into a module six hardening rounds went
183
183
  * into. Stating the difference is the point: this one is a choice, not an
184
184
  * impossibility.
185
+ *
186
+ * ---------------------------------------------------------------------------
187
+ * VERSION 4, 2026-09-10 (EVID-02) -- THE DECISION IS `reaffirm-refusal`, AND
188
+ * THE FACTUAL BASIS IS TRANSCRIBED HERE RATHER THAN LEFT IN A PLANNING
189
+ * DIRECTORY, for the same reason VERSION 3's paragraph gives.
190
+ *
191
+ * WHAT THE BUMP BUYS: `anno_evid_exec`, the durable, run-identity-keyed table
192
+ * that records ONE fact per row -- an address was OBSERVED EXECUTING during a
193
+ * specific run -- so a later session can query accumulated runtime evidence
194
+ * instead of re-running the program. See `anno-store.ts`'s `DDL` and its
195
+ * `insertExecObservations` / `listExecObservations` / `listObservedRuns` /
196
+ * `deleteExecObservationsForRun`.
197
+ *
198
+ * THE OPTION SELECTED, BY NAME: `reaffirm-refusal`. The strict-equality
199
+ * refusal inside `openStore` stays exactly as VERSION 3 left it -- a
200
+ * version-3 store does not open under this `SCHEMA_VERSION`, no migration arm
201
+ * is written, and the single-witness property (one comparison site, no
202
+ * second write of `anno_meta.schema_version`) is unchanged.
203
+ *
204
+ * THE FACTUAL CHECK, RUN 2026-09-10, SCOPE ONE DEVELOPMENT MACHINE:
205
+ * 1. `find / -xdev -name '*.annostore'`, with `.git` directories excluded --
206
+ * empty. The broader `find "$HOME" -name '*.annostore'` (closing the `-xdev`
207
+ * mount-boundary gap) found five hits, all inside a dated (2026-08-27)
208
+ * scratch probe cache (`~/.cache/gsd-probe/c4/...`), with
209
+ * fixture-pattern names (`t-half`, `t-zero`, `t-tail-100`, `p`, `proj`)
210
+ * and one file zero bytes -- synthetic test fixtures, not a real
211
+ * project's store.
212
+ * 2. `git log --oneline --diff-filter=A -- '*.annostore'` -- empty. No
213
+ * `.annostore` has ever been added to this repository's history.
214
+ * 3. Release tags versus when the store landed: `anno-store.ts` was added
215
+ * by `4c9cea3c` (2026-08-27). `v0.7.0` (2026-09-01) and `v0.8.0`
216
+ * (2026-09-06) BOTH POSTDATE that landing and the VERSION 3 bump
217
+ * itself. **THIS CORRECTS THE VERSION 3 PARAGRAPH ABOVE**: its stated
218
+ * justification -- "the last release tag (`v0.5.0`, 2026-08-25)
219
+ * PREDATES the store entirely -- so no tagged release has ever shipped
220
+ * a store at all" -- was true when written (2026-08-29) and is STALE
221
+ * now. Two tagged releases since then could, in principle, have shipped
222
+ * a working store to a real user.
223
+ * 4. Nothing outside that dated scratch/probe cache directory, and nothing
224
+ * tracked in git, was found on this machine.
225
+ *
226
+ * THE RE-AFFIRMATION IS MADE DESPITE THAT CORRECTION, on the strength of the
227
+ * observed NULL RESULT (zero real stores found), not on the strength of the
228
+ * now-stale "no release ever shipped one" framing. What changed since VERSION
229
+ * 3 is the *possibility* of a field store, not an observation of one, and the
230
+ * two directions' costs are asymmetric in reversibility: re-affirming refusal
231
+ * STAYS REVERSIBLE -- a migration arm can be added later, at version 5,
232
+ * against real evidence -- while shipping a mutating open path now adds a
233
+ * half-reasoned migration to a module that also has a corruption-refusal
234
+ * path, exactly where silent corruption lives, inside a plan whose actual job
235
+ * is an evidence table rather than a schema-migration redesign. House style
236
+ * settles the shape too: this project's standing pattern for a mismatched or
237
+ * missing external state is DETECT, THEN REFUSE BY NAME WITH THE REMEDY IN
238
+ * THE MESSAGE (`CLAUDE.md`'s "external tools are never auto-installed"
239
+ * constraint states the same discipline for a different kind of missing
240
+ * state) -- `openStore`'s refusal message is extended to name that remedy
241
+ * explicitly and to say the refused file is left untouched, so a user who
242
+ * hits it is not left inferring either fact.
243
+ *
244
+ * THE FACTUAL BASIS IS ONE MACHINE ONLY, and the limit travels with the
245
+ * decision: this is not a global guarantee that no field store exists --
246
+ * exactly the scope VERSION 3's own paragraph already accepted once.
247
+ *
248
+ * THE VERSION 1 PARAGRAPH ABOVE IS STILL THE PRECEDENT THIS IS MEASURED
249
+ * AGAINST, and this refusal is, again, a CHOICE and not an impossibility:
250
+ * version 4 COULD be given a migration arm and deliberately is not, for the
251
+ * reasons above.
252
+ *
253
+ * THE REVERSAL CONDITION, RECORDED SO THIS DOES NOT QUIETLY HARDEN INTO
254
+ * PRECEDENT: a migration arm at version 5 is justified if a real, non-scratch
255
+ * `.annostore` file is found in the field -- a user's own project, a bug
256
+ * report attaching one, or a support request -- or if a documented
257
+ * distribution channel is shown to have shipped a version-3 or version-4
258
+ * store to users who have not yet upgraded past it. Absent that evidence, the
259
+ * refusal stays the default.
260
+ *
261
+ * ---------------------------------------------------------------------------
262
+ * VERSION 5, 2026-09-11 (BUILD-07) -- THE DECISION IS `reaffirm-refusal`, THE
263
+ * SAME OPTION VERSION 4 SELECTED, AND THE FACTUAL BASIS IS TRANSCRIBED HERE
264
+ * RATHER THAN LEFT IN A PLANNING DIRECTORY, for the same reason every prior
265
+ * version's paragraph gives.
266
+ *
267
+ * WHAT THE BUMP BUYS: `anno_excluded_range`, the durable record of a
268
+ * user-requested exclusion -- its extent AND the reason the user gave -- so
269
+ * the export can emit the range's real bytes tagged with a visible marker
270
+ * comment instead of a hole, which is `BUILD-07`'s whole invariant: an
271
+ * exclusion is a RECORDED, VISIBLE fact, never a silent drop. See
272
+ * `anno-store.ts`'s `DDL` and its three verbs, `addExcludedRange` /
273
+ * `listExcludedRanges` / `removeExcludedRange`.
274
+ *
275
+ * THE OPTION SELECTED, BY NAME: `reaffirm-refusal`. The strict-equality
276
+ * refusal inside `openStore` stays EXACTLY as VERSION 4 left it -- this bump
277
+ * does not touch that comparison site at all: a version-4 store does not open
278
+ * under this `SCHEMA_VERSION`, no migration arm is written, and the
279
+ * single-witness property (one comparison site, no second write of
280
+ * `anno_meta.schema_version`) is unchanged.
281
+ *
282
+ * THE FACTUAL CHECK, RUN 2026-09-11, SCOPE ONE DEVELOPMENT MACHINE -- the
283
+ * same three checks VERSION 4 ran, re-run rather than inherited, with the
284
+ * REAL results transcribed as of today:
285
+ * 1. `find "$HOME" -name '*.annostore'`, with `.git` directories excluded --
286
+ * SIX hits, not VERSION 4's five. The same five fixture-pattern files
287
+ * inside the dated (2026-08-27) scratch probe cache (`t-half`, `t-zero`,
288
+ * `t-tail-100`, `p`, `proj`) are still present, UNCHANGED. The SIXTH is
289
+ * NEW since VERSION 4's check: `.c64-re-tools/phase45-scratch/
290
+ * tracer.annostore`, inside THIS repository's own gitignored scratch
291
+ * directory, timestamped 2026-09-10 -- its path and name identify it as
292
+ * Phase 45's own tracer/scratch fixture, not a real consuming project's
293
+ * store. Six real files, zero real stores.
294
+ * 2. `git log --oneline --diff-filter=A -- '*.annostore'` -- still empty.
295
+ * No `.annostore` has ever been added to this repository's tracked
296
+ * history.
297
+ * 3. Release tags versus when the store landed: `anno-store.ts` was added
298
+ * by `4c9cea3c` (2026-08-27). Since VERSION 4's check, ONE MORE tagged
299
+ * release has been cut -- `v0.9.0` (2026-09-10) -- joining `v0.7.0`
300
+ * (2026-09-01) and `v0.8.0` (2026-09-06). All THREE now postdate the
301
+ * store's landing and VERSION 3's original bump, so three tagged
302
+ * releases, not two, could in principle have shipped a working store to
303
+ * a real user. No result differs in KIND from VERSION 4's; the count of
304
+ * possible-exposure releases has grown by one, consistent with time
305
+ * having passed rather than with any new evidence of a real store.
306
+ *
307
+ * THE RE-AFFIRMATION IS MADE ON THE SAME BASIS VERSION 4 USED: the observed
308
+ * NULL RESULT (zero real, non-scratch stores found across six candidate
309
+ * files), not on a claim that no release could have shipped one. The new
310
+ * sixth file changes nothing about that null result -- it is this project's
311
+ * OWN scratch output, in a directory this project's own tooling writes to and
312
+ * that ships nothing.
313
+ *
314
+ * WHY A NEW TABLE WAS CHOSEN OVER A NULLABLE `excluded`/`exclusion_reason`
315
+ * COLUMN PAIR ON `anno_range`, recorded because it is the one part of this
316
+ * decision that is structural rather than stylistic: an exclusion's extent is
317
+ * chosen by the USER and has no reason to coincide with a typed range's
318
+ * boundaries, so a column on `anno_range` would force `retype()`'s carve to
319
+ * split, duplicate, or lose an exclusion's reason every time a range boundary
320
+ * moved underneath it -- `retype()` knows nothing about exclusions today and
321
+ * a column would force it to. A separate table keeps the user's own words
322
+ * independent of the store's typing churn entirely. This also happens to
323
+ * match the project's existing preference (`anno_evid_exec` at VERSION 4 over
324
+ * widening `anno_range`), but that precedent is the WEAKER of the two
325
+ * arguments -- the structural one above is the one that would still hold even
326
+ * if this project had no such precedent.
327
+ *
328
+ * THE REVERSAL CONDITION, RECORDED SO THIS DOES NOT QUIETLY HARDEN INTO
329
+ * PRECEDENT: a migration arm at version 6 is justified if a real, non-scratch
330
+ * `.annostore` is found in the field -- a user's own project, a bug report
331
+ * attaching one, or a support request -- or if a documented distribution
332
+ * channel is shown to have shipped a version-4 or version-5 store to users
333
+ * who have not yet upgraded past it. Absent that evidence the refusal stays
334
+ * the default.
185
335
  */
186
- export const SCHEMA_VERSION = 3;
336
+ export const SCHEMA_VERSION = 5;
187
337
 
188
338
  /** The 6510's address space, inclusive at both ends. */
189
339
  export const ADDRESS_MIN = 0x0000;
@@ -464,6 +614,23 @@ export interface ScopeRow {
464
614
  endInclusive: number;
465
615
  }
466
616
 
617
+ /** One user-requested exclusion as the store holds it, added at
618
+ * `SCHEMA_VERSION` 5 (`BUILD-07`). Both ends are INCLUSIVE, matching every
619
+ * other range-shaped row this store persists. There is no `bank` column,
620
+ * because an exclusion is a statement about a span of the SUBJECT PROGRAM the
621
+ * user asked to leave out of the rebuild -- not a memory view -- and there is
622
+ * no confidence, verdict, grade or severity column, because this row holds
623
+ * WHAT THE USER ASKED FOR and nothing the tool concluded about it. A
624
+ * judgement column here would be the exact "tool is the decider" shape
625
+ * `BUILD-05`'s 2026-09-10 rewording removed: the store answers "what did the
626
+ * user record", never "should this range be excluded". */
627
+ export interface ExcludedRangeRow {
628
+ id: number;
629
+ start: number;
630
+ endInclusive: number;
631
+ reason: string;
632
+ }
633
+
467
634
  /** One project-local enum as the store holds it. `variants` is keyed by the
468
635
  * numeric-string forms the schema names -- decimal, `0x`/`$` hex, `0b`/`%`
469
636
  * binary -- and its values are variant names. */
@@ -505,6 +672,81 @@ export interface XrefRow {
505
672
  bank: number | null;
506
673
  }
507
674
 
675
+ /**
676
+ * The three memory regions `memmapshow` reports an execute observation
677
+ * against, `SCHEMA_VERSION` 4 (EVID-01). This is the ONE place this
678
+ * vocabulary is written down.
679
+ *
680
+ * ALL THREE ARE INCLUDED DELIBERATELY, not for symmetry: `AccessFlags`
681
+ * (`textmon-memmap.ts`) carries `read`/`write`/`execute` as three
682
+ * INDEPENDENT bits per region, and dropping any one of the three regions
683
+ * here would discard a genuine positive observation the wire already
684
+ * reported -- `io.execute` is exactly as real a fact as `ram.execute`.
685
+ */
686
+ export const EVID_SOURCE_BANKS = Object.freeze(["ram", "rom", "io"] as const);
687
+
688
+ /** One of the three evidence source banks. */
689
+ export type EvidSourceBank = (typeof EVID_SOURCE_BANKS)[number];
690
+
691
+ /**
692
+ * The runtime evidence layer's own classification of an address, `SCHEMA_VERSION`
693
+ * 4 (EVID-04). This is a TYPE-LEVEL control, not a runtime check: the union has
694
+ * exactly two members and NO `"data"` member exists for a caller to return,
695
+ * mistakenly or otherwise.
696
+ *
697
+ * THIS IS THE SAME DISCIPLINE `textmon-memmap.ts` STATES FOR ITSELF, carried
698
+ * forward rather than re-derived: that module's own header forbids adding
699
+ * "a field, key, label or enum member anywhere in this module or its answer
700
+ * types that classifies an address as DATA on the strength of never having
701
+ * been observed" (`textmon-memmap.ts:32-38`). `BlockClass`'s three-valued
702
+ * union (`"code" | "data" | "undefined"`, `block-class.ts`) is the byte-derived
703
+ * classifier's own vocabulary and is DELIBERATELY NOT REUSED here: that union
704
+ * exists to name a byte-derived guess, and this one exists to name only what
705
+ * was actually witnessed executing. `"unobserved"` means exactly "no
706
+ * execution was ever recorded at this address" -- never "this address is
707
+ * data" -- and no code path in this layer may treat the two as
708
+ * interchangeable (see the ban on deriving `data` from absence, Pitfall 3 in
709
+ * this phase's research).
710
+ */
711
+ export type RuntimeExecClass = "code" | "unobserved";
712
+
713
+ /**
714
+ * One runtime-execution observation as the store holds it (`anno_evid_exec`,
715
+ * `SCHEMA_VERSION` 4). Keyed by the bare run-identity triple
716
+ * `(imageSha256, argvDigest, seed)` plus `address` plus `sourceBank` -- the
717
+ * `no-change` assumption-delta decision plan 43-01's live A/B selected
718
+ * (`docs/phase43-instrumentation-perturbation-ab.md`): there is no
719
+ * `run_class` discriminator column, because instrumentation was measured
720
+ * `no-perturbation` at anchor hit depths 10 and 50.
721
+ *
722
+ * A row here asserts exactly ONE fact: this address was observed executing,
723
+ * in this source bank, during this run. There is no `bank`-reserved column
724
+ * (unlike the annotation tables) and no nullable column -- every field is a
725
+ * fact this table is licensed to assert, or the row does not exist.
726
+ */
727
+ export interface EvidExecRow {
728
+ id: number;
729
+ imageSha256: string;
730
+ argvDigest: string;
731
+ seed: string;
732
+ address: number;
733
+ sourceBank: EvidSourceBank;
734
+ }
735
+
736
+ /**
737
+ * One distinct run identity's accumulated observation count, as
738
+ * `listObservedRuns()` reports it. `observationCount` is a COUNT, never a
739
+ * percentage or rate -- see `listObservedRuns`'s own doc comment in
740
+ * `anno-store.ts` for the denominator this count is a fraction of (EVID-04:
741
+ * a count with no denominator invites the reading "the rest is data").
742
+ */
743
+ export interface ObservedRunRow {
744
+ imageSha256: string;
745
+ argvDigest: string;
746
+ seed: string;
747
+ observationCount: number;
748
+ }
749
+
508
750
  /** What `resolveSplitTargets()` returns: the entry count, the resolved 16-bit
509
751
  * targets in table order, and whether this layout produces cross-references at
510
752
  * all. Nothing here is ever written to disk. */
@@ -1300,6 +1542,37 @@ export function assertAccessKind(value: unknown): XrefAccessKind {
1300
1542
  return assertMember(value, XREF_ACCESS_KINDS, "access kind");
1301
1543
  }
1302
1544
 
1545
+ /** Narrows an unvalidated argument to an `EvidSourceBank`, or throws
1546
+ * `AnnoTypeError` carrying the offending value and all three valid members. */
1547
+ export function assertEvidSourceBank(value: unknown): EvidSourceBank {
1548
+ return assertMember(value, EVID_SOURCE_BANKS, "source bank");
1549
+ }
1550
+
1551
+ /** Exactly 64 lowercase hex characters -- the shape `argvDigest()`
1552
+ * (`capture-predicate.ts`) and a sha256 image hash both already produce.
1553
+ * Refuses anything else BY NAME, including a correctly-shaped but
1554
+ * UPPERCASE digest, rather than lower-casing it silently: two callers
1555
+ * disagreeing on case is exactly the kind of divergence a store write must
1556
+ * not launder into agreement. */
1557
+ export function assertRunIdentityDigest(value: unknown, what: string): string {
1558
+ if (typeof value === "string" && /^[0-9a-f]{64}$/.test(value)) {
1559
+ return value;
1560
+ }
1561
+ throw new AnnoTypeError(
1562
+ `${what} ${JSON.stringify(value)} is not exactly 64 lowercase hex characters -- expected a sha256 hex digest such as argvDigest() or an image hash produces`,
1563
+ { dataType: value },
1564
+ );
1565
+ }
1566
+
1567
+ /** A non-empty string. `seed` is not a digest and carries no shape beyond
1568
+ * that -- REPRO-04's run-identity composite treats it as an opaque token. */
1569
+ export function assertRunIdentitySeed(value: unknown): string {
1570
+ if (typeof value === "string" && value.length > 0) {
1571
+ return value;
1572
+ }
1573
+ throw new AnnoTypeError(`seed ${JSON.stringify(value)} is not a non-empty string`, { dataType: value });
1574
+ }
1575
+
1303
1576
  /** The legal-identifier shape, quoted from the schema's own sentence
1304
1577
  * (`anno-tools.ts:246-251`): "starts with a letter or underscore, followed by
1305
1578
  * letters/digits/underscores only". Used for label names and for project-enum