@maximtop/opencode-debug-mode 0.1.0

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 (71) hide show
  1. package/ATTRIBUTION.md +5 -0
  2. package/CONTRIBUTING.md +48 -0
  3. package/LICENSE +21 -0
  4. package/README.md +76 -0
  5. package/SECURITY.md +17 -0
  6. package/assets/debug-agent.md +71 -0
  7. package/dist/chunk-2PCBVVWX.js +20 -0
  8. package/dist/chunk-2PCBVVWX.js.map +1 -0
  9. package/dist/cleanup/export.d.ts +23 -0
  10. package/dist/cleanup/service.d.ts +33 -0
  11. package/dist/cleanup/types.d.ts +235 -0
  12. package/dist/collector/auth.d.ts +3 -0
  13. package/dist/collector/body.d.ts +7 -0
  14. package/dist/collector/ingest.d.ts +8 -0
  15. package/dist/collector/router.d.ts +8 -0
  16. package/dist/collector/server.d.ts +25 -0
  17. package/dist/core/clock.d.ts +5 -0
  18. package/dist/core/constants.d.ts +20 -0
  19. package/dist/core/errors.d.ts +12 -0
  20. package/dist/core/result.d.ts +24 -0
  21. package/dist/core/schemas.d.ts +9 -0
  22. package/dist/errors-IQTPGK5R.js +7 -0
  23. package/dist/errors-IQTPGK5R.js.map +1 -0
  24. package/dist/evidence/read.d.ts +8 -0
  25. package/dist/evidence/sanitize.d.ts +9 -0
  26. package/dist/evidence/store.d.ts +65 -0
  27. package/dist/evidence/types.d.ts +83 -0
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +3652 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/investigation/schema.d.ts +184 -0
  32. package/dist/investigation/store.d.ts +30 -0
  33. package/dist/plugin.d.ts +8 -0
  34. package/dist/probes/extension-permissions.d.ts +8 -0
  35. package/dist/probes/helper.d.ts +22 -0
  36. package/dist/probes/registry.d.ts +18 -0
  37. package/dist/probes/remove.d.ts +16 -0
  38. package/dist/probes/template.d.ts +21 -0
  39. package/dist/probes/types.d.ts +24 -0
  40. package/dist/process/line-decoder.d.ts +26 -0
  41. package/dist/process/protocol.d.ts +40 -0
  42. package/dist/process/service.d.ts +46 -0
  43. package/dist/process/tree.d.ts +16 -0
  44. package/dist/process-supervisor.js +276 -0
  45. package/dist/process-supervisor.js.map +1 -0
  46. package/dist/run/service.d.ts +21 -0
  47. package/dist/session/atomic-json.d.ts +1 -0
  48. package/dist/session/manifest-store.d.ts +22 -0
  49. package/dist/session/orphan-recovery.d.ts +16 -0
  50. package/dist/session/paths.d.ts +11 -0
  51. package/dist/session/registry.d.ts +48 -0
  52. package/dist/session/secret-store.d.ts +7 -0
  53. package/dist/session/types.d.ts +296 -0
  54. package/dist/tools/cleanup-tool.d.ts +4 -0
  55. package/dist/tools/collector-tools.d.ts +17 -0
  56. package/dist/tools/common.d.ts +4 -0
  57. package/dist/tools/evidence-tools.d.ts +4 -0
  58. package/dist/tools/index.d.ts +30 -0
  59. package/dist/tools/probe-tools.d.ts +5 -0
  60. package/dist/tools/run-tools.d.ts +12 -0
  61. package/dist/tools/session-tools.d.ts +4 -0
  62. package/dist/tools/state-tools.d.ts +4 -0
  63. package/docs/architecture.md +15 -0
  64. package/docs/lifecycle.md +11 -0
  65. package/examples/chrome-extension/background.js +5 -0
  66. package/examples/chrome-extension/content.js +3 -0
  67. package/examples/cli/run.mjs +3 -0
  68. package/examples/firefox-extension/background.js +4 -0
  69. package/examples/firefox-extension/content.js +3 -0
  70. package/examples/web/app.js +5 -0
  71. package/package.json +80 -0
package/ATTRIBUTION.md ADDED
@@ -0,0 +1,5 @@
1
+ # Attribution
2
+
3
+ OpenCode documentation, Node.js documentation, npm documentation, Vitest documentation, and community debugging projects were studied as references for public APIs and general design ideas.
4
+
5
+ No third-party source code was copied into this repository. If code is selectively ported later, the same change must add its exact source file, license, copyright notice, and required attribution here before merge.
@@ -0,0 +1,48 @@
1
+ # Contributing
2
+
3
+ Use Node.js 20 or newer and npm. Install reproducibly with `npm ci`.
4
+
5
+ Before submitting a change, run:
6
+
7
+ ```sh
8
+ npm run lint
9
+ npm run typecheck
10
+ npm test
11
+ npm run test:integration
12
+ npm run test:e2e
13
+ npm run coverage
14
+ npm run build
15
+ npm pack --dry-run
16
+ ```
17
+
18
+ Behavior changes should begin with a failing focused test. Preserve the stable public tool names, machine-readable envelopes, strict schemas, bounded data flow, trusted-session isolation, and exact ownership cleanup. Do not add a new evidence transport or probe language without cross-platform syntax, end-to-end capture, verification, cleanup, security, and stress coverage.
19
+
20
+ ## Releases
21
+
22
+ Pushing a supported version tag runs one automated pipeline: prepare and test the package, create the GitHub Release, then publish the exact prepared tarball to npm with provenance. The tag and `package.json` version must match exactly:
23
+
24
+ - `vX.Y.Z` publishes with npm dist-tag `latest`.
25
+ - `vX.Y.Z-beta.N` creates a prerelease and publishes with npm dist-tag `beta`.
26
+ - `vX.Y.Z-rc.N` creates a prerelease and publishes with npm dist-tag `next`.
27
+
28
+ Only tag commits that belong to `master`. For a normal patch release:
29
+
30
+ ```sh
31
+ npm version patch --no-git-tag-version
32
+ git add package.json package-lock.json
33
+ git commit -m "Bump version to X.Y.Z"
34
+ git push origin master
35
+ # Wait for the CI workflow on master to pass.
36
+ git tag -a vX.Y.Z -m "vX.Y.Z"
37
+ git push origin vX.Y.Z
38
+ ```
39
+
40
+ Use the same sequence with an exact `X.Y.Z-beta.N` or `X.Y.Z-rc.N` version for a prerelease. Do not move or reuse a published tag or version.
41
+
42
+ The release workflow is safe to rerun. It accepts an existing GitHub Release only when the tag, prerelease state, tarball, and checksum match. If the npm version already exists, its registry integrity must match the prepared manifest; any mismatch is reported as a collision. A failed npm publish leaves the GitHub Release in place, so rerun only the failed job.
43
+
44
+ ### First npm publication
45
+
46
+ Before pushing the first tag, make the repository public and create the GitHub Environment `npm` without manual approval, restricted to protected tag pattern `v*`. Create a short-lived npm granular token with read/write access to the `@maximtop` scope and bypass 2FA, then save it only as the environment secret `NPM_TOKEN`. The bootstrap token is necessary because the npm package does not exist yet.
47
+
48
+ After the first successful publication, configure an npm Trusted Publisher with user `maximtop`, repository `opencode-debug-mode`, workflow `release.yml`, environment `npm`, and permission `npm publish`. Then remove the `NODE_AUTH_TOKEN` binding from the workflow, delete the `NPM_TOKEN` GitHub secret, revoke the granular token, and enable npm's "2FA and disallow tokens" mode. Later releases authenticate only through GitHub OIDC and continue to receive provenance automatically.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 opencode-debug-mode contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # opencode-debug-mode
2
+
3
+ `@maximtop/opencode-debug-mode` is an explicit, hypothesis-driven runtime debugging agent for OpenCode. It registers a selectable `debug` primary agent, the `/debug` command, and a bounded structured tool surface for CLI, web, and browser-extension investigations.
4
+
5
+ ## Requirements
6
+
7
+ - OpenCode 1.17 or any compatible 1.x release
8
+ - Node.js 20 or newer
9
+ - An npm-compatible OpenCode plugin installation
10
+
11
+ ## Install
12
+
13
+ Install the package from the public npm registry in the environment where OpenCode resolves plugins:
14
+
15
+ ```sh
16
+ npm install @maximtop/opencode-debug-mode
17
+ ```
18
+
19
+ Then add its npm name to the normal OpenCode configuration:
20
+
21
+ ```json
22
+ {
23
+ "plugin": ["@maximtop/opencode-debug-mode"]
24
+ }
25
+ ```
26
+
27
+ Restart OpenCode, select the `debug` agent or run `/debug describe the runtime failure`. No agent or command files need to be copied.
28
+
29
+ ## Workflow
30
+
31
+ The agent records scope and two to four falsifiable hypotheses, captures a failing `pre-fix` baseline, adds the smallest owned probe, analyzes correlated evidence, applies only the confirmed fix, creates a distinct `post-fix` run, and cleans every owned resource. Durable revisioned state allows the workflow to resume after compaction or restart without repeating conclusive checks.
32
+
33
+ CLI targets run below a watchdog supervisor. Web targets use an authenticated server bound only to loopback. Extension content scripts relay through the existing messaging style; only the background helper can reach loopback.
34
+
35
+ See the runnable patterns in:
36
+
37
+ - `examples/cli`
38
+ - `examples/web`
39
+ - `examples/chrome-extension`
40
+ - `examples/firefox-extension`
41
+
42
+ ## Privacy, retention, and cleanup
43
+
44
+ The default is `keepArtifacts=false`. Evidence, state, credentials, manifests, helpers, probes, permissions, listeners, and owned processes are removed on completion or abandonment. Nothing is uploaded and the package emits no telemetry.
45
+
46
+ Explicit retention requires a destination. The exported bundle contains sanitized NDJSON evidence, a sanitized checkpoint, a report, and public hashes. It never includes the bearer credential, internal cleanup manifest, raw environment, or ephemeral path inventory.
47
+
48
+ Cleanup removes only byte-for-byte owned marker blocks, exact-hash helper files, and extension permissions added by the active session. Ambiguous user-edited content is preserved and reported as partial cleanup.
49
+
50
+ ## Limits
51
+
52
+ - 64 KiB per collector request and 100 events per batch
53
+ - 8 KiB control/scalar values
54
+ - 25,000 events and 25 MiB evidence per session
55
+ - 256 KiB durable checkpoint
56
+ - 30-minute idle expiry, suppressed only by a process or explicit reproduction-wait lease
57
+ - Three no-signal investigation iterations
58
+
59
+ ## v1 boundaries
60
+
61
+ Generated probes target JavaScript and TypeScript only. The two transports are supervised process capture and authenticated loopback HTTP ingestion. There is no evidence-reading HTTP endpoint, remote collector, debugger-protocol backend, or automatic activation.
62
+
63
+ ## Troubleshooting
64
+
65
+ - If startup reports a bind error, verify local software is not denying both IPv4 and IPv6 loopback sockets.
66
+ - If a probe cannot register, reapply the exact marker returned by the preparation tool and retry the project parse/type/build check.
67
+ - If cleanup is partial, preserve the reported marker or permission and review it manually; unrelated edits are intentionally never overwritten.
68
+ - If a checkpoint is invalid or incompatible, follow the explicit recovery result rather than restarting the investigation silently.
69
+
70
+ ## Uninstall
71
+
72
+ Finish or abandon active investigations so cleanup can run, remove `@maximtop/opencode-debug-mode` from the OpenCode `plugin` list, uninstall the npm package, and restart OpenCode.
73
+
74
+ ## License
75
+
76
+ MIT. See `LICENSE`.
package/SECURITY.md ADDED
@@ -0,0 +1,17 @@
1
+ # Security policy
2
+
3
+ ## Threat model
4
+
5
+ Runtime evidence and target processes are untrusted. Loopback is a network boundary, not authentication: every data/health request requires a random 256-bit bearer credential. Only a minimal unauthenticated CORS preflight is allowed. The collector never exposes evidence reading or deletion over HTTP.
6
+
7
+ Credentials live only in mode-0600 ephemeral storage, collector memory, and an exact-hash-owned background/web helper. They are excluded from manifests, checkpoints, tool results, logs, reports, and retained bundles. Authentication compares fixed-length digests.
8
+
9
+ Input bodies, headers, schemas, IDs, paths, strings, collections, event counts, files, and queues are bounded. Evidence is recursively sanitized with deterministic secret-key redaction before persistence. Runtime IDs never become path components.
10
+
11
+ Commands use executable/argument arrays with no shell. The watchdog terminates descendant trees if the parent disappears. Commands involving credentials, devices, external state, materially different actions, or external directories require OpenCode permission approval.
12
+
13
+ Cleanup owns exact marker bytes, helper hashes, and individual permission entries; it never restores a whole source file. Ambiguous changes are preserved and reported. Explicitly retained evidence remains sensitive even after sanitization and should receive the same access controls as source code.
14
+
15
+ ## Reporting a vulnerability
16
+
17
+ Use the repository's GitHub private vulnerability reporting feature. Do not open a public issue containing a credential, exploit, or captured evidence. Include affected versions, reproduction conditions, impact, and a minimal proof without real user data.
@@ -0,0 +1,71 @@
1
+ # Hypothesis-driven runtime debugging
2
+
3
+ You are the explicit debug agent. Use runtime evidence to confirm or eliminate falsifiable explanations, preserve the investigation independently of conversation context, apply only an evidence-backed fix, verify it against a baseline, and deterministically remove every owned resource.
4
+
5
+ ## Entry and suitability
6
+
7
+ Activate this workflow only when the developer selects the `debug` agent or invokes `/debug`. Do not auto-activate for generic errors. Call `debug_state_read` before the first action of every resumed turn, after compaction, and after an OpenCode restart. Reconcile the stored revision, phase, checks, evidence references, and next action before doing anything else.
8
+
9
+ If a trivial error is already directly proven and needs no runtime evidence, explain that and offer ordinary debugging before starting a debug session. Never claim runtime confirmation from static analysis alone.
10
+
11
+ ## Scope checkpoint
12
+
13
+ Call `debug_session_start`. Record the problem summary, expected and actual behavior, runtime target, reproduction method, whether a person must reproduce it, and measurable success criteria. Persist the complete state with `debug_state_checkpoint` before waiting or editing behavioral code. Checkpoint after every meaningful transition: hypotheses, completed checks and interpretations, instrumentation changes, reproduction confirmation, evidence decisions, fix decisions, verification, and cleanup.
14
+
15
+ ## Hypotheses
16
+
17
+ Before a behavioral fix, record two to four ranked falsifiable hypotheses. Give each explicit confirmation and elimination signals. The only exception is one existing runtime trace that directly proves a single cause; record its evidence ID as the single-cause reference. Static analysis may rank hypotheses but cannot confirm them.
18
+
19
+ ## Baseline
20
+
21
+ Create a `pre-fix` run with `debug_run_start`. Capture the failing baseline before changing behavior. Use `debug_process_capture` for a CLI target, or start the collector and prepare the relevant runtime transport for a human-reproduced target. Record whether the failure actually reproduced.
22
+
23
+ ## Instrumentation
24
+
25
+ Add the minimum probe needed to distinguish the ranked hypotheses. Use the exact marker block returned by `debug_probe_prepare`; do not synthesize IDs, markers, endpoints, credentials, or expressions. Captures must be identifier/property paths. Register the exact edit with `debug_probe_register`, then run the most relevant parse, type, or build check with `debug_process_capture` using `instrumentation-check` before reproduction. Use sampling or aggregation for hot paths; never instrument every item in an unbounded loop.
26
+
27
+ Only JavaScript and TypeScript probes are supported. Extension content code must relay through its selected message adapter; background code owns loopback transport.
28
+
29
+ ## Human reproduction
30
+
31
+ Immediately before requesting user action, checkpoint phase `waiting_for_reproduction`, the waiting run, and the precise next action. Ask only for actions inside the target application. Do not ask the developer to inspect the collector. Do not ask for ports, health checks, local files, console output, or copied logs. After the reply, call `debug_state_read`, then `debug_evidence_read`, and record whether reproduction occurred before classifying evidence.
32
+
33
+ ## Evidence decisions
34
+
35
+ For every hypothesis, record `open`, `confirmed`, or `eliminated` with stable evidence IDs. Keep raw payloads and complete output in the bounded evidence store, not the checkpoint. If an iteration yields no deciding signal, checkpoint the interpretation, increment `loopIteration`, and change the probe or reproduction deliberately. Stop after three no-signal iterations. Offer a materially different approach, escalation, or abandonment; do not repeat the same inconclusive check.
36
+
37
+ ## Fix
38
+
39
+ Change only the cause supported by deciding evidence. Identify masking, feature-disabling, or bypass changes as workarounds and request explicit approval before applying them. Checkpoint the selected fix, evidence, and changed files before editing.
40
+
41
+ ## Verification
42
+
43
+ Create a distinct `post-fix` run. Repeat the same reproduction and relevant regression, build, type, and lint checks. Compare pre-fix and post-fix evidence, not only exit status. Ensure instrumentation did not mask the failure. Record verification results and deciding evidence in the checkpoint.
44
+
45
+ ## Cleanup and report
46
+
47
+ Checkpoint phase `cleaning`. Always call `debug_cleanup` on success, unresolved outcome, abandonment, escalation, or the next safe action after cancellation. Supply the structured outcome, root cause and evidence, final hypothesis statuses, fix and files, verification, and an optional bounded clean-target check. Report the retained artifact location only when explicit retention succeeded. Report every failed or ambiguous cleanup resource. Never say the target is clean when cleanup is partial.
48
+
49
+ ## Resume and invalidation
50
+
51
+ Do not repeat a completed conclusive check unless its checkpoint entry records `invalidatedBy` with a changed condition or newer conflicting evidence. A missing, invalid, or unsupported checkpoint is a recovery condition: report it explicitly and reconstruct only verifiable runtime facts. Never invent lost conclusions or silently restart.
52
+
53
+ ## Secret and scope rules
54
+
55
+ Never request, display, log, checkpoint, or report a bearer token or an ephemeral session path. Never use runtime IDs as filesystem paths. Never access another trusted session. Never add telemetry or send evidence off the local machine. Never enable an unadvertised adapter or language. Use executable and argument arrays, and request approval for credentials, devices, external state, external directories, or materially different actions.
56
+
57
+ ## Phase-to-tool guide
58
+
59
+ | Phase | Tool |
60
+ | --- | --- |
61
+ | Start | `debug_session_start` |
62
+ | Public lifecycle check | `debug_session_status` |
63
+ | Resume/reconcile | `debug_state_read` |
64
+ | Persist transition | `debug_state_checkpoint` |
65
+ | Baseline or verification run | `debug_run_start` |
66
+ | CLI/check execution | `debug_process_capture` |
67
+ | Web/background transport | `debug_collector_start` |
68
+ | Prepare exact instrumentation | `debug_probe_prepare` |
69
+ | Verify marker ownership | `debug_probe_register` |
70
+ | Analyze bounded evidence | `debug_evidence_read` |
71
+ | Teardown and report | `debug_cleanup` |
@@ -0,0 +1,20 @@
1
+ // src/core/errors.ts
2
+ var DebugModeError = class extends Error {
3
+ code;
4
+ retryable;
5
+ action;
6
+ details;
7
+ constructor(code, message, retryable = false, options = {}) {
8
+ super(message.slice(0, 8192));
9
+ this.name = "DebugModeError";
10
+ this.code = code;
11
+ this.retryable = retryable;
12
+ if (options.action !== void 0) this.action = options.action.slice(0, 8192);
13
+ if (options.details !== void 0) this.details = options.details;
14
+ }
15
+ };
16
+
17
+ export {
18
+ DebugModeError
19
+ };
20
+ //# sourceMappingURL=chunk-2PCBVVWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/errors.ts"],"sourcesContent":["export type DebugErrorCode =\n | \"SESSION_EXISTS\"\n | \"DESTINATION_REQUIRED\"\n | \"NODE_UNSUPPORTED\"\n | \"STORAGE_UNAVAILABLE\"\n | \"NO_ACTIVE_SESSION\"\n | \"SESSION_OWNERSHIP_MISMATCH\"\n | \"STATE_MISSING\"\n | \"STATE_INVALID\"\n | \"STATE_VERSION_UNSUPPORTED\"\n | \"STALE_REVISION\"\n | \"STATE_TOO_LARGE\"\n | \"INVALID_PHASE\"\n | \"RUN_LIMIT\"\n | \"RUN_NOT_FOUND\"\n | \"PROBE_NOT_VALIDATED\"\n | \"COMMAND_REQUIRES_APPROVAL\"\n | \"PROCESS_START_FAILED\"\n | \"PROCESS_TIMEOUT\"\n | \"LOOPBACK_BIND_FAILED\"\n | \"COLLECTOR_EXISTS\"\n | \"HELPER_PATH_UNSAFE\"\n | \"UNSAFE_CAPTURE\"\n | \"UNSUPPORTED_LANGUAGE\"\n | \"COLLECTOR_REQUIRED\"\n | \"MARKER_MISSING\"\n | \"MARKER_MISMATCH\"\n | \"PERMISSION_MISMATCH\"\n | \"FILTER_INVALID\"\n | \"EVIDENCE_UNAVAILABLE\"\n | \"CLEANUP_PARTIAL\"\n | \"EXPORT_FAILED\"\n | \"INTERNAL_ERROR\"\n\nexport type SafeErrorDetail = string | number | boolean\n\nexport class DebugModeError extends Error {\n readonly code: DebugErrorCode\n readonly retryable: boolean\n readonly action?: string\n readonly details?: Record<string, SafeErrorDetail>\n\n constructor(\n code: DebugErrorCode,\n message: string,\n retryable = false,\n options: { action?: string; details?: Record<string, SafeErrorDetail> } = {},\n ) {\n super(message.slice(0, 8_192))\n this.name = \"DebugModeError\"\n this.code = code\n this.retryable = retryable\n if (options.action !== undefined) this.action = options.action.slice(0, 8_192)\n if (options.details !== undefined) this.details = options.details\n }\n}\n"],"mappings":";AAoCO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACE,MACA,SACA,YAAY,OACZ,UAA0E,CAAC,GAC3E;AACA,UAAM,QAAQ,MAAM,GAAG,IAAK,CAAC;AAC7B,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,YAAY;AACjB,QAAI,QAAQ,WAAW,OAAW,MAAK,SAAS,QAAQ,OAAO,MAAM,GAAG,IAAK;AAC7E,QAAI,QAAQ,YAAY,OAAW,MAAK,UAAU,QAAQ;AAAA,EAC5D;AACF;","names":[]}
@@ -0,0 +1,23 @@
1
+ import { type CleanupResult, type FinalReportInput } from "./types.js";
2
+ export type RetainedBundleInput = Readonly<{
3
+ keepArtifacts: boolean;
4
+ destination?: string;
5
+ sessionDir: string;
6
+ evidenceFile: string;
7
+ stateFile: string;
8
+ token: string;
9
+ securityValues?: string[];
10
+ finalReport: FinalReportInput;
11
+ }>;
12
+ export type StagedBundle = Readonly<{
13
+ partialPath: string;
14
+ finalPath: string;
15
+ token: string;
16
+ securityValues: string[];
17
+ report: FinalReportInput;
18
+ eventCount: number;
19
+ }>;
20
+ export declare function stageRetainedBundle(input: RetainedBundleInput): Promise<StagedBundle>;
21
+ export declare function finalizeRetainedBundle(staged: StagedBundle, cleanupInput: CleanupResult): Promise<{
22
+ path: string;
23
+ }>;
@@ -0,0 +1,33 @@
1
+ import type { z } from "zod";
2
+ import type { CollectorServer } from "../collector/server.js";
3
+ import type { DebugSession } from "../session/registry.js";
4
+ import type { ProcessManifestSchema } from "../session/types.js";
5
+ import { type CleanupResult, type FinalReportInput } from "./types.js";
6
+ type OwnedProcess = z.infer<typeof ProcessManifestSchema>;
7
+ type ResourceResult = CleanupResult["resources"]["collector"];
8
+ export declare class CleanupService {
9
+ private readonly session;
10
+ private readonly dependencies;
11
+ private running;
12
+ private completed;
13
+ constructor(session: DebugSession, dependencies?: {
14
+ collector?: Pick<CollectorServer, "close">;
15
+ terminateProcess?: (process: OwnedProcess) => Promise<ResourceResult>;
16
+ removeSecret?: () => Promise<"success" | "already-clean">;
17
+ securityValues?: string[];
18
+ });
19
+ run(input: {
20
+ reason: string;
21
+ finalReport: FinalReportInput;
22
+ cleanCheck?: {
23
+ executable: string;
24
+ args: string[];
25
+ cwd: string;
26
+ timeoutMs: number;
27
+ };
28
+ }): Promise<CleanupResult>;
29
+ private execute;
30
+ private removeOwnedFile;
31
+ private runCleanCheck;
32
+ }
33
+ export {};
@@ -0,0 +1,235 @@
1
+ import { z } from "zod";
2
+ export declare const ResourceCleanupResultSchema: z.ZodObject<{
3
+ status: z.ZodEnum<{
4
+ "already-clean": "already-clean";
5
+ failed: "failed";
6
+ skipped: "skipped";
7
+ success: "success";
8
+ }>;
9
+ reason: z.ZodOptional<z.ZodString>;
10
+ location: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strict>;
12
+ export declare const CleanupResultSchema: z.ZodObject<{
13
+ status: z.ZodEnum<{
14
+ complete: "complete";
15
+ partial: "partial";
16
+ }>;
17
+ reason: z.ZodString;
18
+ resources: z.ZodObject<{
19
+ collector: z.ZodObject<{
20
+ status: z.ZodEnum<{
21
+ "already-clean": "already-clean";
22
+ failed: "failed";
23
+ skipped: "skipped";
24
+ success: "success";
25
+ }>;
26
+ reason: z.ZodOptional<z.ZodString>;
27
+ location: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strict>;
29
+ processes: z.ZodArray<z.ZodObject<{
30
+ status: z.ZodEnum<{
31
+ "already-clean": "already-clean";
32
+ failed: "failed";
33
+ skipped: "skipped";
34
+ success: "success";
35
+ }>;
36
+ reason: z.ZodOptional<z.ZodString>;
37
+ location: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strict>>;
39
+ probes: z.ZodArray<z.ZodObject<{
40
+ status: z.ZodEnum<{
41
+ "already-clean": "already-clean";
42
+ failed: "failed";
43
+ skipped: "skipped";
44
+ success: "success";
45
+ }>;
46
+ reason: z.ZodOptional<z.ZodString>;
47
+ location: z.ZodOptional<z.ZodString>;
48
+ }, z.core.$strict>>;
49
+ permissions: z.ZodArray<z.ZodObject<{
50
+ status: z.ZodEnum<{
51
+ "already-clean": "already-clean";
52
+ failed: "failed";
53
+ skipped: "skipped";
54
+ success: "success";
55
+ }>;
56
+ reason: z.ZodOptional<z.ZodString>;
57
+ location: z.ZodOptional<z.ZodString>;
58
+ }, z.core.$strict>>;
59
+ files: z.ZodArray<z.ZodObject<{
60
+ status: z.ZodEnum<{
61
+ "already-clean": "already-clean";
62
+ failed: "failed";
63
+ skipped: "skipped";
64
+ success: "success";
65
+ }>;
66
+ reason: z.ZodOptional<z.ZodString>;
67
+ location: z.ZodOptional<z.ZodString>;
68
+ }, z.core.$strict>>;
69
+ secret: z.ZodObject<{
70
+ status: z.ZodEnum<{
71
+ "already-clean": "already-clean";
72
+ failed: "failed";
73
+ skipped: "skipped";
74
+ success: "success";
75
+ }>;
76
+ reason: z.ZodOptional<z.ZodString>;
77
+ location: z.ZodOptional<z.ZodString>;
78
+ }, z.core.$strict>;
79
+ sessionDirectory: z.ZodObject<{
80
+ status: z.ZodEnum<{
81
+ "already-clean": "already-clean";
82
+ failed: "failed";
83
+ skipped: "skipped";
84
+ success: "success";
85
+ }>;
86
+ reason: z.ZodOptional<z.ZodString>;
87
+ location: z.ZodOptional<z.ZodString>;
88
+ }, z.core.$strict>;
89
+ }, z.core.$strict>;
90
+ remainingArtifacts: z.ZodArray<z.ZodString>;
91
+ durationMs: z.ZodNumber;
92
+ cleanCheck: z.ZodOptional<z.ZodObject<{
93
+ command: z.ZodString;
94
+ exitCode: z.ZodNullable<z.ZodNumber>;
95
+ timedOut: z.ZodBoolean;
96
+ durationMs: z.ZodNumber;
97
+ }, z.core.$strict>>;
98
+ retainedArtifactLocation: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strict>;
100
+ export declare const FinalReportInputSchema: z.ZodObject<{
101
+ outcome: z.ZodEnum<{
102
+ abandoned: "abandoned";
103
+ completed: "completed";
104
+ escalated: "escalated";
105
+ unresolved: "unresolved";
106
+ }>;
107
+ rootCause: z.ZodString;
108
+ decidingEvidence: z.ZodArray<z.ZodString>;
109
+ hypotheses: z.ZodArray<z.ZodObject<{
110
+ id: z.ZodString;
111
+ status: z.ZodEnum<{
112
+ confirmed: "confirmed";
113
+ eliminated: "eliminated";
114
+ open: "open";
115
+ }>;
116
+ statement: z.ZodString;
117
+ }, z.core.$strict>>;
118
+ fix: z.ZodString;
119
+ changedFiles: z.ZodArray<z.ZodString>;
120
+ verification: z.ZodArray<z.ZodString>;
121
+ }, z.core.$strict>;
122
+ export declare const FinalReportSchema: z.ZodObject<{
123
+ outcome: z.ZodEnum<{
124
+ abandoned: "abandoned";
125
+ completed: "completed";
126
+ escalated: "escalated";
127
+ unresolved: "unresolved";
128
+ }>;
129
+ rootCause: z.ZodString;
130
+ decidingEvidence: z.ZodArray<z.ZodString>;
131
+ hypotheses: z.ZodArray<z.ZodObject<{
132
+ id: z.ZodString;
133
+ status: z.ZodEnum<{
134
+ confirmed: "confirmed";
135
+ eliminated: "eliminated";
136
+ open: "open";
137
+ }>;
138
+ statement: z.ZodString;
139
+ }, z.core.$strict>>;
140
+ fix: z.ZodString;
141
+ changedFiles: z.ZodArray<z.ZodString>;
142
+ verification: z.ZodArray<z.ZodString>;
143
+ cleanup: z.ZodObject<{
144
+ status: z.ZodEnum<{
145
+ complete: "complete";
146
+ partial: "partial";
147
+ }>;
148
+ reason: z.ZodString;
149
+ resources: z.ZodObject<{
150
+ collector: z.ZodObject<{
151
+ status: z.ZodEnum<{
152
+ "already-clean": "already-clean";
153
+ failed: "failed";
154
+ skipped: "skipped";
155
+ success: "success";
156
+ }>;
157
+ reason: z.ZodOptional<z.ZodString>;
158
+ location: z.ZodOptional<z.ZodString>;
159
+ }, z.core.$strict>;
160
+ processes: z.ZodArray<z.ZodObject<{
161
+ status: z.ZodEnum<{
162
+ "already-clean": "already-clean";
163
+ failed: "failed";
164
+ skipped: "skipped";
165
+ success: "success";
166
+ }>;
167
+ reason: z.ZodOptional<z.ZodString>;
168
+ location: z.ZodOptional<z.ZodString>;
169
+ }, z.core.$strict>>;
170
+ probes: z.ZodArray<z.ZodObject<{
171
+ status: z.ZodEnum<{
172
+ "already-clean": "already-clean";
173
+ failed: "failed";
174
+ skipped: "skipped";
175
+ success: "success";
176
+ }>;
177
+ reason: z.ZodOptional<z.ZodString>;
178
+ location: z.ZodOptional<z.ZodString>;
179
+ }, z.core.$strict>>;
180
+ permissions: z.ZodArray<z.ZodObject<{
181
+ status: z.ZodEnum<{
182
+ "already-clean": "already-clean";
183
+ failed: "failed";
184
+ skipped: "skipped";
185
+ success: "success";
186
+ }>;
187
+ reason: z.ZodOptional<z.ZodString>;
188
+ location: z.ZodOptional<z.ZodString>;
189
+ }, z.core.$strict>>;
190
+ files: z.ZodArray<z.ZodObject<{
191
+ status: z.ZodEnum<{
192
+ "already-clean": "already-clean";
193
+ failed: "failed";
194
+ skipped: "skipped";
195
+ success: "success";
196
+ }>;
197
+ reason: z.ZodOptional<z.ZodString>;
198
+ location: z.ZodOptional<z.ZodString>;
199
+ }, z.core.$strict>>;
200
+ secret: z.ZodObject<{
201
+ status: z.ZodEnum<{
202
+ "already-clean": "already-clean";
203
+ failed: "failed";
204
+ skipped: "skipped";
205
+ success: "success";
206
+ }>;
207
+ reason: z.ZodOptional<z.ZodString>;
208
+ location: z.ZodOptional<z.ZodString>;
209
+ }, z.core.$strict>;
210
+ sessionDirectory: z.ZodObject<{
211
+ status: z.ZodEnum<{
212
+ "already-clean": "already-clean";
213
+ failed: "failed";
214
+ skipped: "skipped";
215
+ success: "success";
216
+ }>;
217
+ reason: z.ZodOptional<z.ZodString>;
218
+ location: z.ZodOptional<z.ZodString>;
219
+ }, z.core.$strict>;
220
+ }, z.core.$strict>;
221
+ remainingArtifacts: z.ZodArray<z.ZodString>;
222
+ durationMs: z.ZodNumber;
223
+ cleanCheck: z.ZodOptional<z.ZodObject<{
224
+ command: z.ZodString;
225
+ exitCode: z.ZodNullable<z.ZodNumber>;
226
+ timedOut: z.ZodBoolean;
227
+ durationMs: z.ZodNumber;
228
+ }, z.core.$strict>>;
229
+ retainedArtifactLocation: z.ZodOptional<z.ZodString>;
230
+ }, z.core.$strict>;
231
+ retainedArtifactLocation: z.ZodOptional<z.ZodString>;
232
+ }, z.core.$strict>;
233
+ export type CleanupResult = z.infer<typeof CleanupResultSchema>;
234
+ export type FinalReportInput = z.infer<typeof FinalReportInputSchema>;
235
+ export type FinalReport = z.infer<typeof FinalReportSchema>;
@@ -0,0 +1,3 @@
1
+ import type { SecretStore } from "../session/secret-store.js";
2
+ export declare function authenticateBearer(header: string | undefined, expectedToken: string): boolean;
3
+ export declare function createCollectorCredential(store: SecretStore): Promise<string>;
@@ -0,0 +1,7 @@
1
+ import type { IncomingMessage } from "node:http";
2
+ export declare class CollectorBodyError extends Error {
3
+ readonly status: 400 | 413 | 415;
4
+ readonly code: "INVALID_REQUEST" | "LIMIT_EXCEEDED" | "UNSUPPORTED_MEDIA_TYPE";
5
+ constructor(status: 400 | 413 | 415, code: "INVALID_REQUEST" | "LIMIT_EXCEEDED" | "UNSUPPORTED_MEDIA_TYPE");
6
+ }
7
+ export declare function readBoundedJsonBody(request: IncomingMessage): Promise<unknown>;
@@ -0,0 +1,8 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { EvidenceStore } from "../evidence/store.js";
3
+ import { type EventInput } from "../evidence/types.js";
4
+ export declare function createIngestHandler(options: {
5
+ evidence: EvidenceStore;
6
+ validateEvent: (event: EventInput) => Promise<EventInput>;
7
+ sample?: (event: EventInput) => boolean | Promise<boolean>;
8
+ }): (request: IncomingMessage, response: ServerResponse, origin?: string) => Promise<void>;
@@ -0,0 +1,8 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ export type IngestHandler = (request: IncomingMessage, response: ServerResponse, origin?: string) => Promise<void>;
3
+ export declare function createCollectorRouter(options: {
4
+ token: string;
5
+ ingest?: IngestHandler;
6
+ onAuthenticated?: () => void | Promise<void>;
7
+ }): (request: IncomingMessage, response: ServerResponse, status: () => "ready" | "draining") => Promise<void>;
8
+ export declare function writeCollectorJson(response: ServerResponse, status: number, value: unknown, origin?: string): void;
@@ -0,0 +1,25 @@
1
+ import { type IncomingMessage, type ServerResponse } from "node:http";
2
+ export type CollectorStatus = "stopped" | "starting" | "ready" | "draining" | "failed";
3
+ export type CollectorRequestHandler = (request: IncomingMessage, response: ServerResponse, status: () => "ready" | "draining") => void | Promise<void>;
4
+ export type CollectorHandle = Readonly<{
5
+ id: string;
6
+ host: "127.0.0.1" | "::1";
7
+ port: number;
8
+ status: "ready";
9
+ close(): Promise<void>;
10
+ }>;
11
+ export declare class CollectorServer {
12
+ private readonly handler;
13
+ private readonly onFailure?;
14
+ private server;
15
+ private readonly sockets;
16
+ private state;
17
+ private handle;
18
+ private failureReported;
19
+ constructor(handler?: CollectorRequestHandler, onFailure?: ((reason: string) => void | Promise<void>) | undefined);
20
+ start(): Promise<CollectorHandle>;
21
+ get status(): CollectorStatus;
22
+ close(): Promise<void>;
23
+ private bind;
24
+ private reportFailure;
25
+ }
@@ -0,0 +1,5 @@
1
+ export interface Clock {
2
+ now(): Date;
3
+ monotonicMs(): number;
4
+ }
5
+ export declare const systemClock: Clock;
@@ -0,0 +1,20 @@
1
+ export declare const PACKAGE_ID: "opencode-debug-mode";
2
+ export declare const MANIFEST_SCHEMA_VERSION: 1;
3
+ export declare const STATE_SCHEMA_VERSION: 1;
4
+ export declare const EVENT_SCHEMA_VERSION: 1;
5
+ export declare const TEMP_BASE_NAME: "opencode-debug-mode-v1";
6
+ export declare const PROCESS_EVENT_PREFIX: "__OPENCODE_DEBUG_EVENT_V1__";
7
+ export declare const LIMITS: Readonly<{
8
+ requestBytes: number;
9
+ scalarBytes: number;
10
+ events: 25000;
11
+ evidenceBytes: number;
12
+ checkpointBytes: number;
13
+ eventsPerBatch: 100;
14
+ idleMs: number;
15
+ collectorReadyMs: 2000;
16
+ cleanupMs: 5000;
17
+ gracefulKillMs: 750;
18
+ forcedKillMs: 1500;
19
+ noSignalIterations: 3;
20
+ }>;