c64-debug-mcp 1.0.12 → 1.0.14

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.
package/dist/http.cjs CHANGED
@@ -66,7 +66,7 @@ var stopReasonSchema = import_zod.z.enum([
66
66
  ]);
67
67
  var sessionHealthSchema = import_zod.z.enum(["not_configured", "starting", "ready", "recovering", "stopped", "error"]);
68
68
  var breakpointKindSchema = import_zod.z.enum(["exec", "read", "write", "read_write"]);
69
- var resetModeSchema = import_zod.z.enum(["soft", "hard"]);
69
+ var resetModeSchema = import_zod.z.enum(["soft", "hard", "nuclear"]);
70
70
  var inputActionSchema = import_zod.z.enum(["press", "release", "tap"]);
71
71
  var joystickControlSchema = import_zod.z.enum(["up", "down", "left", "right", "fire"]);
72
72
  var joystickPortSchema = import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2)]);
@@ -1728,7 +1728,17 @@ var ViceSession = class {
1728
1728
  }
1729
1729
  this.#syncMonitorRuntimeState();
1730
1730
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1731
- void this.#scheduleRecovery();
1731
+ void this.#scheduleRecovery().catch((error) => {
1732
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
1733
+ });
1734
+ }
1735
+ });
1736
+ this.#client.on("transport-error", (error) => {
1737
+ this.#writeProcessLogLine(`[monitor-error] transport error: ${error.message}`);
1738
+ if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1739
+ void this.#scheduleRecovery().catch((recoveryError) => {
1740
+ this.#writeProcessLogLine(`[recovery-error] ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
1741
+ });
1732
1742
  }
1733
1743
  });
1734
1744
  this.#client.on("event", (event) => {
@@ -2069,6 +2079,10 @@ var ViceSession = class {
2069
2079
  async resetMachine(mode) {
2070
2080
  return this.#withExecutionLock(async () => {
2071
2081
  await this.#ensureReady();
2082
+ this.#clearHeldInputState();
2083
+ if (mode === "nuclear") {
2084
+ return await this.#performNuclearReset();
2085
+ }
2072
2086
  const wasPaused = this.#explicitPauseActive;
2073
2087
  this.#lastExecutionIntent = "reset";
2074
2088
  this.#writeProcessLogLine(`[tx] execute reset mode=${mode}`);
@@ -2088,6 +2102,67 @@ var ViceSession = class {
2088
2102
  };
2089
2103
  });
2090
2104
  }
2105
+ async #performNuclearReset() {
2106
+ const savedBreakpoints = await this.#captureBreakpointState();
2107
+ const wasPaused = this.#explicitPauseActive;
2108
+ this.#writeProcessLogLine("[nuclear-reset] initiating full VICE process restart");
2109
+ await this.#scheduleRecovery();
2110
+ await this.#restoreBreakpointState(savedBreakpoints);
2111
+ this.#explicitPauseActive = wasPaused;
2112
+ if (wasPaused) {
2113
+ this.#writeProcessLogLine("[nuclear-reset] restoring paused state");
2114
+ await this.pauseExecution();
2115
+ }
2116
+ this.#writeProcessLogLine("[nuclear-reset] completed successfully");
2117
+ const debugState = await this.#readDebugState();
2118
+ return {
2119
+ executionState: debugState.executionState,
2120
+ lastStopReason: debugState.lastStopReason,
2121
+ programCounter: debugState.programCounter,
2122
+ registers: debugState.registers,
2123
+ warnings: []
2124
+ };
2125
+ }
2126
+ async #captureBreakpointState() {
2127
+ try {
2128
+ const result = await this.listBreakpoints(true);
2129
+ return result.breakpoints.map((bp) => ({
2130
+ kind: bp.kind,
2131
+ start: bp.start,
2132
+ end: bp.end,
2133
+ enabled: bp.enabled,
2134
+ temporary: bp.temporary,
2135
+ hasCondition: bp.hasCondition,
2136
+ label: this.#breakpointLabels.get(bp.id) ?? null
2137
+ }));
2138
+ } catch (error) {
2139
+ this.#writeProcessLogLine(`[nuclear-reset] failed to capture breakpoints: ${error instanceof Error ? error.message : String(error)}`);
2140
+ return [];
2141
+ }
2142
+ }
2143
+ async #restoreBreakpointState(savedBreakpoints) {
2144
+ if (savedBreakpoints.length === 0) {
2145
+ return;
2146
+ }
2147
+ this.#writeProcessLogLine(`[nuclear-reset] restoring ${savedBreakpoints.length} breakpoints`);
2148
+ for (const bp of savedBreakpoints) {
2149
+ try {
2150
+ await this.breakpointSet({
2151
+ kind: bp.kind,
2152
+ address: bp.start,
2153
+ length: bp.end - bp.start + 1,
2154
+ enabled: bp.enabled,
2155
+ temporary: bp.temporary,
2156
+ label: bp.label ?? void 0
2157
+ });
2158
+ this.#writeProcessLogLine(`[nuclear-reset] restored breakpoint at $${bp.start.toString(16).toUpperCase()}`);
2159
+ } catch (error) {
2160
+ this.#writeProcessLogLine(
2161
+ `[nuclear-reset] failed to restore breakpoint at $${bp.start.toString(16).toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`
2162
+ );
2163
+ }
2164
+ }
2165
+ }
2091
2166
  async listBreakpoints(includeDisabled = true) {
2092
2167
  await this.#ensureReady();
2093
2168
  this.#writeProcessLogLine(`[tx] breakpoint_list includeDisabled=${includeDisabled}`);
@@ -2802,7 +2877,9 @@ var ViceSession = class {
2802
2877
  makeWarning(`C64 emulator process exited (${code ?? "null"} / ${signal ?? "null"})`, "process_exit")
2803
2878
  ];
2804
2879
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2805
- void this.#scheduleRecovery();
2880
+ void this.#scheduleRecovery().catch((error) => {
2881
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
2882
+ });
2806
2883
  }
2807
2884
  });
2808
2885
  child.once("error", (error) => {
@@ -2815,12 +2892,16 @@ var ViceSession = class {
2815
2892
  this.#transportState = "faulted";
2816
2893
  this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "process_error"), makeWarning(error.message, "process_error")];
2817
2894
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2818
- void this.#scheduleRecovery();
2895
+ void this.#scheduleRecovery().catch((error2) => {
2896
+ this.#writeProcessLogLine(`[recovery-error] ${error2 instanceof Error ? error2.message : String(error2)}`);
2897
+ });
2819
2898
  }
2820
2899
  });
2821
2900
  }
2822
2901
  #attachProcessLogging(child, binary, args) {
2823
2902
  const logStream = (0, import_node_fs.createWriteStream)(VICE_PROCESS_LOG_PATH, { flags: "a" });
2903
+ logStream.on("error", () => {
2904
+ });
2824
2905
  this.#processLogStream = logStream;
2825
2906
  this.#stdoutMirrorBuffer = "";
2826
2907
  this.#stderrMirrorBuffer = "";
@@ -2854,7 +2935,10 @@ var ViceSession = class {
2854
2935
  logStream.write(`
2855
2936
  === Emulator stream closed ${nowIso()} (${reason}) ===
2856
2937
  `);
2857
- logStream.end();
2938
+ logStream.end(() => {
2939
+ });
2940
+ logStream.on("error", () => {
2941
+ });
2858
2942
  this.#processLogStream = null;
2859
2943
  }
2860
2944
  #mirrorViceOutputChunk(stream, chunk) {
@@ -3636,40 +3720,35 @@ var setRegistersTool = createViceTool({
3636
3720
  });
3637
3721
  var readMemoryTool = createViceTool({
3638
3722
  id: "memory_read",
3639
- description: "Reads a memory chunk. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3640
- inputSchema: import_zod4.z.union([
3641
- import_zod4.z.object({
3642
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3643
- length: import_zod4.z.number().int().positive().max(65535).describe("Number of bytes to read")
3644
- }).refine((input) => input.address + input.length <= 65536, {
3645
- message: "address + length must stay within the 64K address space",
3646
- path: ["length"]
3647
- }),
3648
- import_zod4.z.object({
3649
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3650
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)")
3651
- }).refine((input) => input.start <= input.end, {
3652
- message: "End address must be greater than or equal to start address",
3653
- path: ["end"]
3654
- }).refine((input) => input.end < 65536, {
3655
- message: "End address must stay within the 64K address space",
3656
- path: ["end"]
3657
- })
3658
- ]),
3723
+ description: "Reads a memory chunk. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3724
+ inputSchema: import_zod4.z.object({
3725
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3726
+ data_length: import_zod4.z.number().int().positive().max(65535).optional().describe("Number of bytes to read (use either data_length or end_address)"),
3727
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address.")
3728
+ }),
3659
3729
  dataSchema: import_zod4.z.object({
3660
- address: import_zod4.z.number().int().min(0).max(65535).describe("Start address of the returned memory chunk"),
3730
+ address: address16Schema.describe("Start address of the returned memory chunk"),
3661
3731
  length: import_zod4.z.number().int().min(0).describe("Number of bytes returned"),
3662
3732
  data: byteArraySchema.describe("Raw bytes returned from memory")
3663
3733
  }),
3664
3734
  execute: async (input) => {
3665
- let address;
3735
+ const address = input.start_address;
3666
3736
  let length;
3667
- if ("start" in input && "end" in input) {
3668
- address = input.start;
3669
- length = input.end - input.start + 1;
3737
+ if (input.end_address !== void 0) {
3738
+ if (address > input.end_address) {
3739
+ throw new Error("End address must be greater than or equal to start address");
3740
+ }
3741
+ if (input.end_address >= 65536) {
3742
+ throw new Error("End address must stay within the 64K address space");
3743
+ }
3744
+ length = input.end_address - address + 1;
3745
+ } else if (input.data_length !== void 0) {
3746
+ length = input.data_length;
3747
+ if (address + length > 65536) {
3748
+ throw new Error("address + length must stay within the 64K address space");
3749
+ }
3670
3750
  } else {
3671
- address = input.address;
3672
- length = input.length;
3751
+ throw new Error("Must provide either data_length or end_address");
3673
3752
  }
3674
3753
  const result = await c64Session.readMemory(address, address + length - 1);
3675
3754
  return {
@@ -3691,7 +3770,7 @@ var writeMemoryTool = createViceTool({
3691
3770
  }),
3692
3771
  dataSchema: import_zod4.z.object({
3693
3772
  worked: import_zod4.z.boolean().describe("Whether the write operation completed successfully"),
3694
- address: import_zod4.z.number().int().min(0).max(65535).describe("Start address where the bytes were written"),
3773
+ address: address16Schema.describe("Start address where the bytes were written"),
3695
3774
  length: import_zod4.z.number().int().min(1).describe("Number of bytes written")
3696
3775
  }).extend(debugStateSchema.shape),
3697
3776
  execute: async (input) => await c64Session.writeMemory(input.address, input.data)
@@ -3702,7 +3781,7 @@ var executeTool = createViceTool({
3702
3781
  inputSchema: import_zod4.z.object({
3703
3782
  action: import_zod4.z.enum(["pause", "resume", "step", "step_over", "step_out", "reset"]),
3704
3783
  count: import_zod4.z.number().int().positive().default(1).describe("Instruction count for step and step_over actions"),
3705
- resetMode: resetModeSchema.default("soft").describe("Reset mode when action is reset"),
3784
+ resetMode: resetModeSchema.default("soft").describe("Reset mode: soft (CPU reset), hard (full machine reset), or nuclear (complete VICE restart)"),
3706
3785
  waitUntilRunningStable: import_zod4.z.boolean().default(false).describe("When action is resume, wait until running becomes stable before returning")
3707
3786
  }),
3708
3787
  dataSchema: debugStateSchema.extend({
@@ -3740,47 +3819,44 @@ var listBreakpointsTool = createViceTool({
3740
3819
  });
3741
3820
  var breakpointSetTool = createViceTool({
3742
3821
  id: "breakpoint_set",
3743
- description: "Creates an execution breakpoint or read/write watchpoint. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3744
- inputSchema: import_zod4.z.union([
3745
- import_zod4.z.object({
3746
- kind: breakpointKindSchema,
3747
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3748
- length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
3749
- condition: import_zod4.z.string().optional(),
3750
- label: import_zod4.z.string().optional(),
3751
- temporary: import_zod4.z.boolean().default(false),
3752
- enabled: import_zod4.z.boolean().default(true)
3753
- }),
3754
- import_zod4.z.object({
3755
- kind: breakpointKindSchema,
3756
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3757
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3758
- condition: import_zod4.z.string().optional(),
3759
- label: import_zod4.z.string().optional(),
3760
- temporary: import_zod4.z.boolean().default(false),
3761
- enabled: import_zod4.z.boolean().default(true)
3762
- }).refine((input) => input.start <= input.end, {
3763
- message: "End address must be greater than or equal to start address",
3764
- path: ["end"]
3765
- })
3766
- ]),
3822
+ description: "Creates an execution breakpoint or read/write watchpoint. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3823
+ inputSchema: import_zod4.z.object({
3824
+ kind: breakpointKindSchema,
3825
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3826
+ data_length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes (use either data_length or end_address)"),
3827
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address."),
3828
+ condition: import_zod4.z.string().optional(),
3829
+ label: import_zod4.z.string().optional(),
3830
+ temporary: import_zod4.z.boolean().default(false),
3831
+ enabled: import_zod4.z.boolean().default(true)
3832
+ }),
3767
3833
  dataSchema: import_zod4.z.object({
3768
3834
  breakpoint: breakpointSchema,
3769
3835
  executionState: executionStateSchema,
3770
3836
  lastStopReason: stopReasonSchema,
3771
- programCounter: import_zod4.z.number().int().min(0).max(65535).nullable(),
3837
+ programCounter: address16Schema.nullable(),
3772
3838
  registers: c64PartialRegisterValueSchema.nullable()
3773
3839
  }),
3774
3840
  execute: async (input) => {
3775
- const normalizedInput = "start" in input && "end" in input ? {
3841
+ const address = input.start_address;
3842
+ let length;
3843
+ if (input.end_address !== void 0) {
3844
+ if (address > input.end_address) {
3845
+ throw new Error("End address must be greater than or equal to start address");
3846
+ }
3847
+ length = input.end_address - address + 1;
3848
+ } else {
3849
+ length = input.data_length;
3850
+ }
3851
+ const normalizedInput = {
3776
3852
  kind: input.kind,
3777
- address: input.start,
3778
- length: input.end - input.start + 1,
3853
+ address,
3854
+ length,
3779
3855
  condition: input.condition,
3780
3856
  label: input.label,
3781
3857
  temporary: input.temporary,
3782
3858
  enabled: input.enabled
3783
- } : input;
3859
+ };
3784
3860
  const result = await c64Session.breakpointSet(normalizedInput);
3785
3861
  return {
3786
3862
  breakpoint: normalizeBreakpoint(result.breakpoint),
package/dist/http.js CHANGED
@@ -43,7 +43,7 @@ var stopReasonSchema = z.enum([
43
43
  ]);
44
44
  var sessionHealthSchema = z.enum(["not_configured", "starting", "ready", "recovering", "stopped", "error"]);
45
45
  var breakpointKindSchema = z.enum(["exec", "read", "write", "read_write"]);
46
- var resetModeSchema = z.enum(["soft", "hard"]);
46
+ var resetModeSchema = z.enum(["soft", "hard", "nuclear"]);
47
47
  var inputActionSchema = z.enum(["press", "release", "tap"]);
48
48
  var joystickControlSchema = z.enum(["up", "down", "left", "right", "fire"]);
49
49
  var joystickPortSchema = z.union([z.literal(1), z.literal(2)]);
@@ -1705,7 +1705,17 @@ var ViceSession = class {
1705
1705
  }
1706
1706
  this.#syncMonitorRuntimeState();
1707
1707
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1708
- void this.#scheduleRecovery();
1708
+ void this.#scheduleRecovery().catch((error) => {
1709
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
1710
+ });
1711
+ }
1712
+ });
1713
+ this.#client.on("transport-error", (error) => {
1714
+ this.#writeProcessLogLine(`[monitor-error] transport error: ${error.message}`);
1715
+ if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1716
+ void this.#scheduleRecovery().catch((recoveryError) => {
1717
+ this.#writeProcessLogLine(`[recovery-error] ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
1718
+ });
1709
1719
  }
1710
1720
  });
1711
1721
  this.#client.on("event", (event) => {
@@ -2046,6 +2056,10 @@ var ViceSession = class {
2046
2056
  async resetMachine(mode) {
2047
2057
  return this.#withExecutionLock(async () => {
2048
2058
  await this.#ensureReady();
2059
+ this.#clearHeldInputState();
2060
+ if (mode === "nuclear") {
2061
+ return await this.#performNuclearReset();
2062
+ }
2049
2063
  const wasPaused = this.#explicitPauseActive;
2050
2064
  this.#lastExecutionIntent = "reset";
2051
2065
  this.#writeProcessLogLine(`[tx] execute reset mode=${mode}`);
@@ -2065,6 +2079,67 @@ var ViceSession = class {
2065
2079
  };
2066
2080
  });
2067
2081
  }
2082
+ async #performNuclearReset() {
2083
+ const savedBreakpoints = await this.#captureBreakpointState();
2084
+ const wasPaused = this.#explicitPauseActive;
2085
+ this.#writeProcessLogLine("[nuclear-reset] initiating full VICE process restart");
2086
+ await this.#scheduleRecovery();
2087
+ await this.#restoreBreakpointState(savedBreakpoints);
2088
+ this.#explicitPauseActive = wasPaused;
2089
+ if (wasPaused) {
2090
+ this.#writeProcessLogLine("[nuclear-reset] restoring paused state");
2091
+ await this.pauseExecution();
2092
+ }
2093
+ this.#writeProcessLogLine("[nuclear-reset] completed successfully");
2094
+ const debugState = await this.#readDebugState();
2095
+ return {
2096
+ executionState: debugState.executionState,
2097
+ lastStopReason: debugState.lastStopReason,
2098
+ programCounter: debugState.programCounter,
2099
+ registers: debugState.registers,
2100
+ warnings: []
2101
+ };
2102
+ }
2103
+ async #captureBreakpointState() {
2104
+ try {
2105
+ const result = await this.listBreakpoints(true);
2106
+ return result.breakpoints.map((bp) => ({
2107
+ kind: bp.kind,
2108
+ start: bp.start,
2109
+ end: bp.end,
2110
+ enabled: bp.enabled,
2111
+ temporary: bp.temporary,
2112
+ hasCondition: bp.hasCondition,
2113
+ label: this.#breakpointLabels.get(bp.id) ?? null
2114
+ }));
2115
+ } catch (error) {
2116
+ this.#writeProcessLogLine(`[nuclear-reset] failed to capture breakpoints: ${error instanceof Error ? error.message : String(error)}`);
2117
+ return [];
2118
+ }
2119
+ }
2120
+ async #restoreBreakpointState(savedBreakpoints) {
2121
+ if (savedBreakpoints.length === 0) {
2122
+ return;
2123
+ }
2124
+ this.#writeProcessLogLine(`[nuclear-reset] restoring ${savedBreakpoints.length} breakpoints`);
2125
+ for (const bp of savedBreakpoints) {
2126
+ try {
2127
+ await this.breakpointSet({
2128
+ kind: bp.kind,
2129
+ address: bp.start,
2130
+ length: bp.end - bp.start + 1,
2131
+ enabled: bp.enabled,
2132
+ temporary: bp.temporary,
2133
+ label: bp.label ?? void 0
2134
+ });
2135
+ this.#writeProcessLogLine(`[nuclear-reset] restored breakpoint at $${bp.start.toString(16).toUpperCase()}`);
2136
+ } catch (error) {
2137
+ this.#writeProcessLogLine(
2138
+ `[nuclear-reset] failed to restore breakpoint at $${bp.start.toString(16).toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`
2139
+ );
2140
+ }
2141
+ }
2142
+ }
2068
2143
  async listBreakpoints(includeDisabled = true) {
2069
2144
  await this.#ensureReady();
2070
2145
  this.#writeProcessLogLine(`[tx] breakpoint_list includeDisabled=${includeDisabled}`);
@@ -2779,7 +2854,9 @@ var ViceSession = class {
2779
2854
  makeWarning(`C64 emulator process exited (${code ?? "null"} / ${signal ?? "null"})`, "process_exit")
2780
2855
  ];
2781
2856
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2782
- void this.#scheduleRecovery();
2857
+ void this.#scheduleRecovery().catch((error) => {
2858
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
2859
+ });
2783
2860
  }
2784
2861
  });
2785
2862
  child.once("error", (error) => {
@@ -2792,12 +2869,16 @@ var ViceSession = class {
2792
2869
  this.#transportState = "faulted";
2793
2870
  this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "process_error"), makeWarning(error.message, "process_error")];
2794
2871
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2795
- void this.#scheduleRecovery();
2872
+ void this.#scheduleRecovery().catch((error2) => {
2873
+ this.#writeProcessLogLine(`[recovery-error] ${error2 instanceof Error ? error2.message : String(error2)}`);
2874
+ });
2796
2875
  }
2797
2876
  });
2798
2877
  }
2799
2878
  #attachProcessLogging(child, binary, args) {
2800
2879
  const logStream = createWriteStream(VICE_PROCESS_LOG_PATH, { flags: "a" });
2880
+ logStream.on("error", () => {
2881
+ });
2801
2882
  this.#processLogStream = logStream;
2802
2883
  this.#stdoutMirrorBuffer = "";
2803
2884
  this.#stderrMirrorBuffer = "";
@@ -2831,7 +2912,10 @@ var ViceSession = class {
2831
2912
  logStream.write(`
2832
2913
  === Emulator stream closed ${nowIso()} (${reason}) ===
2833
2914
  `);
2834
- logStream.end();
2915
+ logStream.end(() => {
2916
+ });
2917
+ logStream.on("error", () => {
2918
+ });
2835
2919
  this.#processLogStream = null;
2836
2920
  }
2837
2921
  #mirrorViceOutputChunk(stream, chunk) {
@@ -3613,40 +3697,35 @@ var setRegistersTool = createViceTool({
3613
3697
  });
3614
3698
  var readMemoryTool = createViceTool({
3615
3699
  id: "memory_read",
3616
- description: "Reads a memory chunk. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3617
- inputSchema: z3.union([
3618
- z3.object({
3619
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3620
- length: z3.number().int().positive().max(65535).describe("Number of bytes to read")
3621
- }).refine((input) => input.address + input.length <= 65536, {
3622
- message: "address + length must stay within the 64K address space",
3623
- path: ["length"]
3624
- }),
3625
- z3.object({
3626
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3627
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)")
3628
- }).refine((input) => input.start <= input.end, {
3629
- message: "End address must be greater than or equal to start address",
3630
- path: ["end"]
3631
- }).refine((input) => input.end < 65536, {
3632
- message: "End address must stay within the 64K address space",
3633
- path: ["end"]
3634
- })
3635
- ]),
3700
+ description: "Reads a memory chunk. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3701
+ inputSchema: z3.object({
3702
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3703
+ data_length: z3.number().int().positive().max(65535).optional().describe("Number of bytes to read (use either data_length or end_address)"),
3704
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address.")
3705
+ }),
3636
3706
  dataSchema: z3.object({
3637
- address: z3.number().int().min(0).max(65535).describe("Start address of the returned memory chunk"),
3707
+ address: address16Schema.describe("Start address of the returned memory chunk"),
3638
3708
  length: z3.number().int().min(0).describe("Number of bytes returned"),
3639
3709
  data: byteArraySchema.describe("Raw bytes returned from memory")
3640
3710
  }),
3641
3711
  execute: async (input) => {
3642
- let address;
3712
+ const address = input.start_address;
3643
3713
  let length;
3644
- if ("start" in input && "end" in input) {
3645
- address = input.start;
3646
- length = input.end - input.start + 1;
3714
+ if (input.end_address !== void 0) {
3715
+ if (address > input.end_address) {
3716
+ throw new Error("End address must be greater than or equal to start address");
3717
+ }
3718
+ if (input.end_address >= 65536) {
3719
+ throw new Error("End address must stay within the 64K address space");
3720
+ }
3721
+ length = input.end_address - address + 1;
3722
+ } else if (input.data_length !== void 0) {
3723
+ length = input.data_length;
3724
+ if (address + length > 65536) {
3725
+ throw new Error("address + length must stay within the 64K address space");
3726
+ }
3647
3727
  } else {
3648
- address = input.address;
3649
- length = input.length;
3728
+ throw new Error("Must provide either data_length or end_address");
3650
3729
  }
3651
3730
  const result = await c64Session.readMemory(address, address + length - 1);
3652
3731
  return {
@@ -3668,7 +3747,7 @@ var writeMemoryTool = createViceTool({
3668
3747
  }),
3669
3748
  dataSchema: z3.object({
3670
3749
  worked: z3.boolean().describe("Whether the write operation completed successfully"),
3671
- address: z3.number().int().min(0).max(65535).describe("Start address where the bytes were written"),
3750
+ address: address16Schema.describe("Start address where the bytes were written"),
3672
3751
  length: z3.number().int().min(1).describe("Number of bytes written")
3673
3752
  }).extend(debugStateSchema.shape),
3674
3753
  execute: async (input) => await c64Session.writeMemory(input.address, input.data)
@@ -3679,7 +3758,7 @@ var executeTool = createViceTool({
3679
3758
  inputSchema: z3.object({
3680
3759
  action: z3.enum(["pause", "resume", "step", "step_over", "step_out", "reset"]),
3681
3760
  count: z3.number().int().positive().default(1).describe("Instruction count for step and step_over actions"),
3682
- resetMode: resetModeSchema.default("soft").describe("Reset mode when action is reset"),
3761
+ resetMode: resetModeSchema.default("soft").describe("Reset mode: soft (CPU reset), hard (full machine reset), or nuclear (complete VICE restart)"),
3683
3762
  waitUntilRunningStable: z3.boolean().default(false).describe("When action is resume, wait until running becomes stable before returning")
3684
3763
  }),
3685
3764
  dataSchema: debugStateSchema.extend({
@@ -3717,47 +3796,44 @@ var listBreakpointsTool = createViceTool({
3717
3796
  });
3718
3797
  var breakpointSetTool = createViceTool({
3719
3798
  id: "breakpoint_set",
3720
- description: "Creates an execution breakpoint or read/write watchpoint. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3721
- inputSchema: z3.union([
3722
- z3.object({
3723
- kind: breakpointKindSchema,
3724
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3725
- length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
3726
- condition: z3.string().optional(),
3727
- label: z3.string().optional(),
3728
- temporary: z3.boolean().default(false),
3729
- enabled: z3.boolean().default(true)
3730
- }),
3731
- z3.object({
3732
- kind: breakpointKindSchema,
3733
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3734
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3735
- condition: z3.string().optional(),
3736
- label: z3.string().optional(),
3737
- temporary: z3.boolean().default(false),
3738
- enabled: z3.boolean().default(true)
3739
- }).refine((input) => input.start <= input.end, {
3740
- message: "End address must be greater than or equal to start address",
3741
- path: ["end"]
3742
- })
3743
- ]),
3799
+ description: "Creates an execution breakpoint or read/write watchpoint. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3800
+ inputSchema: z3.object({
3801
+ kind: breakpointKindSchema,
3802
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3803
+ data_length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes (use either data_length or end_address)"),
3804
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address."),
3805
+ condition: z3.string().optional(),
3806
+ label: z3.string().optional(),
3807
+ temporary: z3.boolean().default(false),
3808
+ enabled: z3.boolean().default(true)
3809
+ }),
3744
3810
  dataSchema: z3.object({
3745
3811
  breakpoint: breakpointSchema,
3746
3812
  executionState: executionStateSchema,
3747
3813
  lastStopReason: stopReasonSchema,
3748
- programCounter: z3.number().int().min(0).max(65535).nullable(),
3814
+ programCounter: address16Schema.nullable(),
3749
3815
  registers: c64PartialRegisterValueSchema.nullable()
3750
3816
  }),
3751
3817
  execute: async (input) => {
3752
- const normalizedInput = "start" in input && "end" in input ? {
3818
+ const address = input.start_address;
3819
+ let length;
3820
+ if (input.end_address !== void 0) {
3821
+ if (address > input.end_address) {
3822
+ throw new Error("End address must be greater than or equal to start address");
3823
+ }
3824
+ length = input.end_address - address + 1;
3825
+ } else {
3826
+ length = input.data_length;
3827
+ }
3828
+ const normalizedInput = {
3753
3829
  kind: input.kind,
3754
- address: input.start,
3755
- length: input.end - input.start + 1,
3830
+ address,
3831
+ length,
3756
3832
  condition: input.condition,
3757
3833
  label: input.label,
3758
3834
  temporary: input.temporary,
3759
3835
  enabled: input.enabled
3760
- } : input;
3836
+ };
3761
3837
  const result = await c64Session.breakpointSet(normalizedInput);
3762
3838
  return {
3763
3839
  breakpoint: normalizeBreakpoint(result.breakpoint),
package/dist/stdio.cjs CHANGED
@@ -63,7 +63,7 @@ var stopReasonSchema = import_zod.z.enum([
63
63
  ]);
64
64
  var sessionHealthSchema = import_zod.z.enum(["not_configured", "starting", "ready", "recovering", "stopped", "error"]);
65
65
  var breakpointKindSchema = import_zod.z.enum(["exec", "read", "write", "read_write"]);
66
- var resetModeSchema = import_zod.z.enum(["soft", "hard"]);
66
+ var resetModeSchema = import_zod.z.enum(["soft", "hard", "nuclear"]);
67
67
  var inputActionSchema = import_zod.z.enum(["press", "release", "tap"]);
68
68
  var joystickControlSchema = import_zod.z.enum(["up", "down", "left", "right", "fire"]);
69
69
  var joystickPortSchema = import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2)]);
@@ -1725,7 +1725,17 @@ var ViceSession = class {
1725
1725
  }
1726
1726
  this.#syncMonitorRuntimeState();
1727
1727
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1728
- void this.#scheduleRecovery();
1728
+ void this.#scheduleRecovery().catch((error) => {
1729
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
1730
+ });
1731
+ }
1732
+ });
1733
+ this.#client.on("transport-error", (error) => {
1734
+ this.#writeProcessLogLine(`[monitor-error] transport error: ${error.message}`);
1735
+ if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1736
+ void this.#scheduleRecovery().catch((recoveryError) => {
1737
+ this.#writeProcessLogLine(`[recovery-error] ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
1738
+ });
1729
1739
  }
1730
1740
  });
1731
1741
  this.#client.on("event", (event) => {
@@ -2066,6 +2076,10 @@ var ViceSession = class {
2066
2076
  async resetMachine(mode) {
2067
2077
  return this.#withExecutionLock(async () => {
2068
2078
  await this.#ensureReady();
2079
+ this.#clearHeldInputState();
2080
+ if (mode === "nuclear") {
2081
+ return await this.#performNuclearReset();
2082
+ }
2069
2083
  const wasPaused = this.#explicitPauseActive;
2070
2084
  this.#lastExecutionIntent = "reset";
2071
2085
  this.#writeProcessLogLine(`[tx] execute reset mode=${mode}`);
@@ -2085,6 +2099,67 @@ var ViceSession = class {
2085
2099
  };
2086
2100
  });
2087
2101
  }
2102
+ async #performNuclearReset() {
2103
+ const savedBreakpoints = await this.#captureBreakpointState();
2104
+ const wasPaused = this.#explicitPauseActive;
2105
+ this.#writeProcessLogLine("[nuclear-reset] initiating full VICE process restart");
2106
+ await this.#scheduleRecovery();
2107
+ await this.#restoreBreakpointState(savedBreakpoints);
2108
+ this.#explicitPauseActive = wasPaused;
2109
+ if (wasPaused) {
2110
+ this.#writeProcessLogLine("[nuclear-reset] restoring paused state");
2111
+ await this.pauseExecution();
2112
+ }
2113
+ this.#writeProcessLogLine("[nuclear-reset] completed successfully");
2114
+ const debugState = await this.#readDebugState();
2115
+ return {
2116
+ executionState: debugState.executionState,
2117
+ lastStopReason: debugState.lastStopReason,
2118
+ programCounter: debugState.programCounter,
2119
+ registers: debugState.registers,
2120
+ warnings: []
2121
+ };
2122
+ }
2123
+ async #captureBreakpointState() {
2124
+ try {
2125
+ const result = await this.listBreakpoints(true);
2126
+ return result.breakpoints.map((bp) => ({
2127
+ kind: bp.kind,
2128
+ start: bp.start,
2129
+ end: bp.end,
2130
+ enabled: bp.enabled,
2131
+ temporary: bp.temporary,
2132
+ hasCondition: bp.hasCondition,
2133
+ label: this.#breakpointLabels.get(bp.id) ?? null
2134
+ }));
2135
+ } catch (error) {
2136
+ this.#writeProcessLogLine(`[nuclear-reset] failed to capture breakpoints: ${error instanceof Error ? error.message : String(error)}`);
2137
+ return [];
2138
+ }
2139
+ }
2140
+ async #restoreBreakpointState(savedBreakpoints) {
2141
+ if (savedBreakpoints.length === 0) {
2142
+ return;
2143
+ }
2144
+ this.#writeProcessLogLine(`[nuclear-reset] restoring ${savedBreakpoints.length} breakpoints`);
2145
+ for (const bp of savedBreakpoints) {
2146
+ try {
2147
+ await this.breakpointSet({
2148
+ kind: bp.kind,
2149
+ address: bp.start,
2150
+ length: bp.end - bp.start + 1,
2151
+ enabled: bp.enabled,
2152
+ temporary: bp.temporary,
2153
+ label: bp.label ?? void 0
2154
+ });
2155
+ this.#writeProcessLogLine(`[nuclear-reset] restored breakpoint at $${bp.start.toString(16).toUpperCase()}`);
2156
+ } catch (error) {
2157
+ this.#writeProcessLogLine(
2158
+ `[nuclear-reset] failed to restore breakpoint at $${bp.start.toString(16).toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`
2159
+ );
2160
+ }
2161
+ }
2162
+ }
2088
2163
  async listBreakpoints(includeDisabled = true) {
2089
2164
  await this.#ensureReady();
2090
2165
  this.#writeProcessLogLine(`[tx] breakpoint_list includeDisabled=${includeDisabled}`);
@@ -2799,7 +2874,9 @@ var ViceSession = class {
2799
2874
  makeWarning(`C64 emulator process exited (${code ?? "null"} / ${signal ?? "null"})`, "process_exit")
2800
2875
  ];
2801
2876
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2802
- void this.#scheduleRecovery();
2877
+ void this.#scheduleRecovery().catch((error) => {
2878
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
2879
+ });
2803
2880
  }
2804
2881
  });
2805
2882
  child.once("error", (error) => {
@@ -2812,12 +2889,16 @@ var ViceSession = class {
2812
2889
  this.#transportState = "faulted";
2813
2890
  this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "process_error"), makeWarning(error.message, "process_error")];
2814
2891
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2815
- void this.#scheduleRecovery();
2892
+ void this.#scheduleRecovery().catch((error2) => {
2893
+ this.#writeProcessLogLine(`[recovery-error] ${error2 instanceof Error ? error2.message : String(error2)}`);
2894
+ });
2816
2895
  }
2817
2896
  });
2818
2897
  }
2819
2898
  #attachProcessLogging(child, binary, args) {
2820
2899
  const logStream = (0, import_node_fs.createWriteStream)(VICE_PROCESS_LOG_PATH, { flags: "a" });
2900
+ logStream.on("error", () => {
2901
+ });
2821
2902
  this.#processLogStream = logStream;
2822
2903
  this.#stdoutMirrorBuffer = "";
2823
2904
  this.#stderrMirrorBuffer = "";
@@ -2851,7 +2932,10 @@ var ViceSession = class {
2851
2932
  logStream.write(`
2852
2933
  === Emulator stream closed ${nowIso()} (${reason}) ===
2853
2934
  `);
2854
- logStream.end();
2935
+ logStream.end(() => {
2936
+ });
2937
+ logStream.on("error", () => {
2938
+ });
2855
2939
  this.#processLogStream = null;
2856
2940
  }
2857
2941
  #mirrorViceOutputChunk(stream, chunk) {
@@ -3633,40 +3717,35 @@ var setRegistersTool = createViceTool({
3633
3717
  });
3634
3718
  var readMemoryTool = createViceTool({
3635
3719
  id: "memory_read",
3636
- description: "Reads a memory chunk. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3637
- inputSchema: import_zod4.z.union([
3638
- import_zod4.z.object({
3639
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3640
- length: import_zod4.z.number().int().positive().max(65535).describe("Number of bytes to read")
3641
- }).refine((input) => input.address + input.length <= 65536, {
3642
- message: "address + length must stay within the 64K address space",
3643
- path: ["length"]
3644
- }),
3645
- import_zod4.z.object({
3646
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3647
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)")
3648
- }).refine((input) => input.start <= input.end, {
3649
- message: "End address must be greater than or equal to start address",
3650
- path: ["end"]
3651
- }).refine((input) => input.end < 65536, {
3652
- message: "End address must stay within the 64K address space",
3653
- path: ["end"]
3654
- })
3655
- ]),
3720
+ description: "Reads a memory chunk. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3721
+ inputSchema: import_zod4.z.object({
3722
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3723
+ data_length: import_zod4.z.number().int().positive().max(65535).optional().describe("Number of bytes to read (use either data_length or end_address)"),
3724
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address.")
3725
+ }),
3656
3726
  dataSchema: import_zod4.z.object({
3657
- address: import_zod4.z.number().int().min(0).max(65535).describe("Start address of the returned memory chunk"),
3727
+ address: address16Schema.describe("Start address of the returned memory chunk"),
3658
3728
  length: import_zod4.z.number().int().min(0).describe("Number of bytes returned"),
3659
3729
  data: byteArraySchema.describe("Raw bytes returned from memory")
3660
3730
  }),
3661
3731
  execute: async (input) => {
3662
- let address;
3732
+ const address = input.start_address;
3663
3733
  let length;
3664
- if ("start" in input && "end" in input) {
3665
- address = input.start;
3666
- length = input.end - input.start + 1;
3734
+ if (input.end_address !== void 0) {
3735
+ if (address > input.end_address) {
3736
+ throw new Error("End address must be greater than or equal to start address");
3737
+ }
3738
+ if (input.end_address >= 65536) {
3739
+ throw new Error("End address must stay within the 64K address space");
3740
+ }
3741
+ length = input.end_address - address + 1;
3742
+ } else if (input.data_length !== void 0) {
3743
+ length = input.data_length;
3744
+ if (address + length > 65536) {
3745
+ throw new Error("address + length must stay within the 64K address space");
3746
+ }
3667
3747
  } else {
3668
- address = input.address;
3669
- length = input.length;
3748
+ throw new Error("Must provide either data_length or end_address");
3670
3749
  }
3671
3750
  const result = await c64Session.readMemory(address, address + length - 1);
3672
3751
  return {
@@ -3688,7 +3767,7 @@ var writeMemoryTool = createViceTool({
3688
3767
  }),
3689
3768
  dataSchema: import_zod4.z.object({
3690
3769
  worked: import_zod4.z.boolean().describe("Whether the write operation completed successfully"),
3691
- address: import_zod4.z.number().int().min(0).max(65535).describe("Start address where the bytes were written"),
3770
+ address: address16Schema.describe("Start address where the bytes were written"),
3692
3771
  length: import_zod4.z.number().int().min(1).describe("Number of bytes written")
3693
3772
  }).extend(debugStateSchema.shape),
3694
3773
  execute: async (input) => await c64Session.writeMemory(input.address, input.data)
@@ -3699,7 +3778,7 @@ var executeTool = createViceTool({
3699
3778
  inputSchema: import_zod4.z.object({
3700
3779
  action: import_zod4.z.enum(["pause", "resume", "step", "step_over", "step_out", "reset"]),
3701
3780
  count: import_zod4.z.number().int().positive().default(1).describe("Instruction count for step and step_over actions"),
3702
- resetMode: resetModeSchema.default("soft").describe("Reset mode when action is reset"),
3781
+ resetMode: resetModeSchema.default("soft").describe("Reset mode: soft (CPU reset), hard (full machine reset), or nuclear (complete VICE restart)"),
3703
3782
  waitUntilRunningStable: import_zod4.z.boolean().default(false).describe("When action is resume, wait until running becomes stable before returning")
3704
3783
  }),
3705
3784
  dataSchema: debugStateSchema.extend({
@@ -3737,47 +3816,44 @@ var listBreakpointsTool = createViceTool({
3737
3816
  });
3738
3817
  var breakpointSetTool = createViceTool({
3739
3818
  id: "breakpoint_set",
3740
- description: "Creates an execution breakpoint or read/write watchpoint. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3741
- inputSchema: import_zod4.z.union([
3742
- import_zod4.z.object({
3743
- kind: breakpointKindSchema,
3744
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3745
- length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
3746
- condition: import_zod4.z.string().optional(),
3747
- label: import_zod4.z.string().optional(),
3748
- temporary: import_zod4.z.boolean().default(false),
3749
- enabled: import_zod4.z.boolean().default(true)
3750
- }),
3751
- import_zod4.z.object({
3752
- kind: breakpointKindSchema,
3753
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3754
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3755
- condition: import_zod4.z.string().optional(),
3756
- label: import_zod4.z.string().optional(),
3757
- temporary: import_zod4.z.boolean().default(false),
3758
- enabled: import_zod4.z.boolean().default(true)
3759
- }).refine((input) => input.start <= input.end, {
3760
- message: "End address must be greater than or equal to start address",
3761
- path: ["end"]
3762
- })
3763
- ]),
3819
+ description: "Creates an execution breakpoint or read/write watchpoint. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3820
+ inputSchema: import_zod4.z.object({
3821
+ kind: breakpointKindSchema,
3822
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3823
+ data_length: import_zod4.z.number().int().positive().default(1).describe("Size of the breakpoint range in bytes (use either data_length or end_address)"),
3824
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address."),
3825
+ condition: import_zod4.z.string().optional(),
3826
+ label: import_zod4.z.string().optional(),
3827
+ temporary: import_zod4.z.boolean().default(false),
3828
+ enabled: import_zod4.z.boolean().default(true)
3829
+ }),
3764
3830
  dataSchema: import_zod4.z.object({
3765
3831
  breakpoint: breakpointSchema,
3766
3832
  executionState: executionStateSchema,
3767
3833
  lastStopReason: stopReasonSchema,
3768
- programCounter: import_zod4.z.number().int().min(0).max(65535).nullable(),
3834
+ programCounter: address16Schema.nullable(),
3769
3835
  registers: c64PartialRegisterValueSchema.nullable()
3770
3836
  }),
3771
3837
  execute: async (input) => {
3772
- const normalizedInput = "start" in input && "end" in input ? {
3838
+ const address = input.start_address;
3839
+ let length;
3840
+ if (input.end_address !== void 0) {
3841
+ if (address > input.end_address) {
3842
+ throw new Error("End address must be greater than or equal to start address");
3843
+ }
3844
+ length = input.end_address - address + 1;
3845
+ } else {
3846
+ length = input.data_length;
3847
+ }
3848
+ const normalizedInput = {
3773
3849
  kind: input.kind,
3774
- address: input.start,
3775
- length: input.end - input.start + 1,
3850
+ address,
3851
+ length,
3776
3852
  condition: input.condition,
3777
3853
  label: input.label,
3778
3854
  temporary: input.temporary,
3779
3855
  enabled: input.enabled
3780
- } : input;
3856
+ };
3781
3857
  const result = await c64Session.breakpointSet(normalizedInput);
3782
3858
  return {
3783
3859
  breakpoint: normalizeBreakpoint(result.breakpoint),
package/dist/stdio.js CHANGED
@@ -40,7 +40,7 @@ var stopReasonSchema = z.enum([
40
40
  ]);
41
41
  var sessionHealthSchema = z.enum(["not_configured", "starting", "ready", "recovering", "stopped", "error"]);
42
42
  var breakpointKindSchema = z.enum(["exec", "read", "write", "read_write"]);
43
- var resetModeSchema = z.enum(["soft", "hard"]);
43
+ var resetModeSchema = z.enum(["soft", "hard", "nuclear"]);
44
44
  var inputActionSchema = z.enum(["press", "release", "tap"]);
45
45
  var joystickControlSchema = z.enum(["up", "down", "left", "right", "fire"]);
46
46
  var joystickPortSchema = z.union([z.literal(1), z.literal(2)]);
@@ -1702,7 +1702,17 @@ var ViceSession = class {
1702
1702
  }
1703
1703
  this.#syncMonitorRuntimeState();
1704
1704
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1705
- void this.#scheduleRecovery();
1705
+ void this.#scheduleRecovery().catch((error) => {
1706
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
1707
+ });
1708
+ }
1709
+ });
1710
+ this.#client.on("transport-error", (error) => {
1711
+ this.#writeProcessLogLine(`[monitor-error] transport error: ${error.message}`);
1712
+ if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
1713
+ void this.#scheduleRecovery().catch((recoveryError) => {
1714
+ this.#writeProcessLogLine(`[recovery-error] ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`);
1715
+ });
1706
1716
  }
1707
1717
  });
1708
1718
  this.#client.on("event", (event) => {
@@ -2043,6 +2053,10 @@ var ViceSession = class {
2043
2053
  async resetMachine(mode) {
2044
2054
  return this.#withExecutionLock(async () => {
2045
2055
  await this.#ensureReady();
2056
+ this.#clearHeldInputState();
2057
+ if (mode === "nuclear") {
2058
+ return await this.#performNuclearReset();
2059
+ }
2046
2060
  const wasPaused = this.#explicitPauseActive;
2047
2061
  this.#lastExecutionIntent = "reset";
2048
2062
  this.#writeProcessLogLine(`[tx] execute reset mode=${mode}`);
@@ -2062,6 +2076,67 @@ var ViceSession = class {
2062
2076
  };
2063
2077
  });
2064
2078
  }
2079
+ async #performNuclearReset() {
2080
+ const savedBreakpoints = await this.#captureBreakpointState();
2081
+ const wasPaused = this.#explicitPauseActive;
2082
+ this.#writeProcessLogLine("[nuclear-reset] initiating full VICE process restart");
2083
+ await this.#scheduleRecovery();
2084
+ await this.#restoreBreakpointState(savedBreakpoints);
2085
+ this.#explicitPauseActive = wasPaused;
2086
+ if (wasPaused) {
2087
+ this.#writeProcessLogLine("[nuclear-reset] restoring paused state");
2088
+ await this.pauseExecution();
2089
+ }
2090
+ this.#writeProcessLogLine("[nuclear-reset] completed successfully");
2091
+ const debugState = await this.#readDebugState();
2092
+ return {
2093
+ executionState: debugState.executionState,
2094
+ lastStopReason: debugState.lastStopReason,
2095
+ programCounter: debugState.programCounter,
2096
+ registers: debugState.registers,
2097
+ warnings: []
2098
+ };
2099
+ }
2100
+ async #captureBreakpointState() {
2101
+ try {
2102
+ const result = await this.listBreakpoints(true);
2103
+ return result.breakpoints.map((bp) => ({
2104
+ kind: bp.kind,
2105
+ start: bp.start,
2106
+ end: bp.end,
2107
+ enabled: bp.enabled,
2108
+ temporary: bp.temporary,
2109
+ hasCondition: bp.hasCondition,
2110
+ label: this.#breakpointLabels.get(bp.id) ?? null
2111
+ }));
2112
+ } catch (error) {
2113
+ this.#writeProcessLogLine(`[nuclear-reset] failed to capture breakpoints: ${error instanceof Error ? error.message : String(error)}`);
2114
+ return [];
2115
+ }
2116
+ }
2117
+ async #restoreBreakpointState(savedBreakpoints) {
2118
+ if (savedBreakpoints.length === 0) {
2119
+ return;
2120
+ }
2121
+ this.#writeProcessLogLine(`[nuclear-reset] restoring ${savedBreakpoints.length} breakpoints`);
2122
+ for (const bp of savedBreakpoints) {
2123
+ try {
2124
+ await this.breakpointSet({
2125
+ kind: bp.kind,
2126
+ address: bp.start,
2127
+ length: bp.end - bp.start + 1,
2128
+ enabled: bp.enabled,
2129
+ temporary: bp.temporary,
2130
+ label: bp.label ?? void 0
2131
+ });
2132
+ this.#writeProcessLogLine(`[nuclear-reset] restored breakpoint at $${bp.start.toString(16).toUpperCase()}`);
2133
+ } catch (error) {
2134
+ this.#writeProcessLogLine(
2135
+ `[nuclear-reset] failed to restore breakpoint at $${bp.start.toString(16).toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`
2136
+ );
2137
+ }
2138
+ }
2139
+ }
2065
2140
  async listBreakpoints(includeDisabled = true) {
2066
2141
  await this.#ensureReady();
2067
2142
  this.#writeProcessLogLine(`[tx] breakpoint_list includeDisabled=${includeDisabled}`);
@@ -2776,7 +2851,9 @@ var ViceSession = class {
2776
2851
  makeWarning(`C64 emulator process exited (${code ?? "null"} / ${signal ?? "null"})`, "process_exit")
2777
2852
  ];
2778
2853
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2779
- void this.#scheduleRecovery();
2854
+ void this.#scheduleRecovery().catch((error) => {
2855
+ this.#writeProcessLogLine(`[recovery-error] ${error instanceof Error ? error.message : String(error)}`);
2856
+ });
2780
2857
  }
2781
2858
  });
2782
2859
  child.once("error", (error) => {
@@ -2789,12 +2866,16 @@ var ViceSession = class {
2789
2866
  this.#transportState = "faulted";
2790
2867
  this.#warnings = [...this.#warnings.filter((warning) => warning.code !== "process_error"), makeWarning(error.message, "process_error")];
2791
2868
  if (!this.#suppressRecovery && !this.#shuttingDown && this.#config) {
2792
- void this.#scheduleRecovery();
2869
+ void this.#scheduleRecovery().catch((error2) => {
2870
+ this.#writeProcessLogLine(`[recovery-error] ${error2 instanceof Error ? error2.message : String(error2)}`);
2871
+ });
2793
2872
  }
2794
2873
  });
2795
2874
  }
2796
2875
  #attachProcessLogging(child, binary, args) {
2797
2876
  const logStream = createWriteStream(VICE_PROCESS_LOG_PATH, { flags: "a" });
2877
+ logStream.on("error", () => {
2878
+ });
2798
2879
  this.#processLogStream = logStream;
2799
2880
  this.#stdoutMirrorBuffer = "";
2800
2881
  this.#stderrMirrorBuffer = "";
@@ -2828,7 +2909,10 @@ var ViceSession = class {
2828
2909
  logStream.write(`
2829
2910
  === Emulator stream closed ${nowIso()} (${reason}) ===
2830
2911
  `);
2831
- logStream.end();
2912
+ logStream.end(() => {
2913
+ });
2914
+ logStream.on("error", () => {
2915
+ });
2832
2916
  this.#processLogStream = null;
2833
2917
  }
2834
2918
  #mirrorViceOutputChunk(stream, chunk) {
@@ -3610,40 +3694,35 @@ var setRegistersTool = createViceTool({
3610
3694
  });
3611
3695
  var readMemoryTool = createViceTool({
3612
3696
  id: "memory_read",
3613
- description: "Reads a memory chunk. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3614
- inputSchema: z3.union([
3615
- z3.object({
3616
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3617
- length: z3.number().int().positive().max(65535).describe("Number of bytes to read")
3618
- }).refine((input) => input.address + input.length <= 65536, {
3619
- message: "address + length must stay within the 64K address space",
3620
- path: ["length"]
3621
- }),
3622
- z3.object({
3623
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3624
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)")
3625
- }).refine((input) => input.start <= input.end, {
3626
- message: "End address must be greater than or equal to start address",
3627
- path: ["end"]
3628
- }).refine((input) => input.end < 65536, {
3629
- message: "End address must stay within the 64K address space",
3630
- path: ["end"]
3631
- })
3632
- ]),
3697
+ description: "Reads a memory chunk. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000). Returns byte values as decimal numbers.",
3698
+ inputSchema: z3.object({
3699
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3700
+ data_length: z3.number().int().positive().max(65535).optional().describe("Number of bytes to read (use either data_length or end_address)"),
3701
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address.")
3702
+ }),
3633
3703
  dataSchema: z3.object({
3634
- address: z3.number().int().min(0).max(65535).describe("Start address of the returned memory chunk"),
3704
+ address: address16Schema.describe("Start address of the returned memory chunk"),
3635
3705
  length: z3.number().int().min(0).describe("Number of bytes returned"),
3636
3706
  data: byteArraySchema.describe("Raw bytes returned from memory")
3637
3707
  }),
3638
3708
  execute: async (input) => {
3639
- let address;
3709
+ const address = input.start_address;
3640
3710
  let length;
3641
- if ("start" in input && "end" in input) {
3642
- address = input.start;
3643
- length = input.end - input.start + 1;
3711
+ if (input.end_address !== void 0) {
3712
+ if (address > input.end_address) {
3713
+ throw new Error("End address must be greater than or equal to start address");
3714
+ }
3715
+ if (input.end_address >= 65536) {
3716
+ throw new Error("End address must stay within the 64K address space");
3717
+ }
3718
+ length = input.end_address - address + 1;
3719
+ } else if (input.data_length !== void 0) {
3720
+ length = input.data_length;
3721
+ if (address + length > 65536) {
3722
+ throw new Error("address + length must stay within the 64K address space");
3723
+ }
3644
3724
  } else {
3645
- address = input.address;
3646
- length = input.length;
3725
+ throw new Error("Must provide either data_length or end_address");
3647
3726
  }
3648
3727
  const result = await c64Session.readMemory(address, address + length - 1);
3649
3728
  return {
@@ -3665,7 +3744,7 @@ var writeMemoryTool = createViceTool({
3665
3744
  }),
3666
3745
  dataSchema: z3.object({
3667
3746
  worked: z3.boolean().describe("Whether the write operation completed successfully"),
3668
- address: z3.number().int().min(0).max(65535).describe("Start address where the bytes were written"),
3747
+ address: address16Schema.describe("Start address where the bytes were written"),
3669
3748
  length: z3.number().int().min(1).describe("Number of bytes written")
3670
3749
  }).extend(debugStateSchema.shape),
3671
3750
  execute: async (input) => await c64Session.writeMemory(input.address, input.data)
@@ -3676,7 +3755,7 @@ var executeTool = createViceTool({
3676
3755
  inputSchema: z3.object({
3677
3756
  action: z3.enum(["pause", "resume", "step", "step_over", "step_out", "reset"]),
3678
3757
  count: z3.number().int().positive().default(1).describe("Instruction count for step and step_over actions"),
3679
- resetMode: resetModeSchema.default("soft").describe("Reset mode when action is reset"),
3758
+ resetMode: resetModeSchema.default("soft").describe("Reset mode: soft (CPU reset), hard (full machine reset), or nuclear (complete VICE restart)"),
3680
3759
  waitUntilRunningStable: z3.boolean().default(false).describe("When action is resume, wait until running becomes stable before returning")
3681
3760
  }),
3682
3761
  dataSchema: debugStateSchema.extend({
@@ -3714,47 +3793,44 @@ var listBreakpointsTool = createViceTool({
3714
3793
  });
3715
3794
  var breakpointSetTool = createViceTool({
3716
3795
  id: "breakpoint_set",
3717
- description: "Creates an execution breakpoint or read/write watchpoint. Use either (address, length) or (start, end) format. Addresses can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3718
- inputSchema: z3.union([
3719
- z3.object({
3720
- kind: breakpointKindSchema,
3721
- address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3722
- length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes"),
3723
- condition: z3.string().optional(),
3724
- label: z3.string().optional(),
3725
- temporary: z3.boolean().default(false),
3726
- enabled: z3.boolean().default(true)
3727
- }),
3728
- z3.object({
3729
- kind: breakpointKindSchema,
3730
- start: address16Schema.describe("Start address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3731
- end: address16Schema.describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3732
- condition: z3.string().optional(),
3733
- label: z3.string().optional(),
3734
- temporary: z3.boolean().default(false),
3735
- enabled: z3.boolean().default(true)
3736
- }).refine((input) => input.start <= input.end, {
3737
- message: "End address must be greater than or equal to start address",
3738
- path: ["end"]
3739
- })
3740
- ]),
3796
+ description: "Creates an execution breakpoint or read/write watchpoint. Specify start_address and either data_length or end_address. Address can be decimal (53248) or hex string with prefix ($D000, 0xD000).",
3797
+ inputSchema: z3.object({
3798
+ kind: breakpointKindSchema,
3799
+ start_address: address16Schema.describe("Start address: decimal (53248) or hex string with prefix ($D000, 0xD000)"),
3800
+ data_length: z3.number().int().positive().default(1).describe("Size of the breakpoint range in bytes (use either data_length or end_address)"),
3801
+ end_address: address16Schema.optional().describe("End address (inclusive): decimal (53248) or hex string with prefix ($D000, 0xD000). Use either data_length or end_address."),
3802
+ condition: z3.string().optional(),
3803
+ label: z3.string().optional(),
3804
+ temporary: z3.boolean().default(false),
3805
+ enabled: z3.boolean().default(true)
3806
+ }),
3741
3807
  dataSchema: z3.object({
3742
3808
  breakpoint: breakpointSchema,
3743
3809
  executionState: executionStateSchema,
3744
3810
  lastStopReason: stopReasonSchema,
3745
- programCounter: z3.number().int().min(0).max(65535).nullable(),
3811
+ programCounter: address16Schema.nullable(),
3746
3812
  registers: c64PartialRegisterValueSchema.nullable()
3747
3813
  }),
3748
3814
  execute: async (input) => {
3749
- const normalizedInput = "start" in input && "end" in input ? {
3815
+ const address = input.start_address;
3816
+ let length;
3817
+ if (input.end_address !== void 0) {
3818
+ if (address > input.end_address) {
3819
+ throw new Error("End address must be greater than or equal to start address");
3820
+ }
3821
+ length = input.end_address - address + 1;
3822
+ } else {
3823
+ length = input.data_length;
3824
+ }
3825
+ const normalizedInput = {
3750
3826
  kind: input.kind,
3751
- address: input.start,
3752
- length: input.end - input.start + 1,
3827
+ address,
3828
+ length,
3753
3829
  condition: input.condition,
3754
3830
  label: input.label,
3755
3831
  temporary: input.temporary,
3756
3832
  enabled: input.enabled
3757
- } : input;
3833
+ };
3758
3834
  const result = await c64Session.breakpointSet(normalizedInput);
3759
3835
  return {
3760
3836
  breakpoint: normalizeBreakpoint(result.breakpoint),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c64-debug-mcp",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Model Context Protocol server for C64 debugging via VICE emulator",
5
5
  "type": "module",
6
6
  "keywords": [