@cortexkit/opencode-magic-context 0.14.0 → 0.14.2

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 (38) hide show
  1. package/README.md +11 -5
  2. package/dist/cli/diagnostics.d.ts +38 -5
  3. package/dist/cli/diagnostics.d.ts.map +1 -1
  4. package/dist/cli/logs.d.ts.map +1 -1
  5. package/dist/cli.js +158 -7
  6. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  7. package/dist/hooks/magic-context/compaction-marker-manager.d.ts +29 -0
  8. package/dist/hooks/magic-context/compaction-marker-manager.d.ts.map +1 -1
  9. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  10. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  11. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  12. package/dist/hooks/magic-context/compartment-runner-types.d.ts +9 -0
  13. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  14. package/dist/hooks/magic-context/drop-stale-reduce-calls.d.ts.map +1 -1
  15. package/dist/hooks/magic-context/hook-handlers.d.ts +0 -2
  16. package/dist/hooks/magic-context/hook-handlers.d.ts.map +1 -1
  17. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  18. package/dist/hooks/magic-context/nudger.d.ts +1 -4
  19. package/dist/hooks/magic-context/nudger.d.ts.map +1 -1
  20. package/dist/hooks/magic-context/sentinel.d.ts +53 -0
  21. package/dist/hooks/magic-context/sentinel.d.ts.map +1 -0
  22. package/dist/hooks/magic-context/strip-content.d.ts +63 -13
  23. package/dist/hooks/magic-context/strip-content.d.ts.map +1 -1
  24. package/dist/hooks/magic-context/strip-structural-noise.d.ts +14 -0
  25. package/dist/hooks/magic-context/strip-structural-noise.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/system-prompt-hash.d.ts +18 -5
  27. package/dist/hooks/magic-context/system-prompt-hash.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -0
  29. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  31. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  32. package/dist/index.js +321 -180
  33. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  34. package/dist/shared/error-message.d.ts +30 -0
  35. package/dist/shared/error-message.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/shared/error-message.test.ts +94 -0
  38. package/src/shared/error-message.ts +112 -0
@@ -1 +1 @@
1
- {"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAmD0f,CAAC;;;;;;;;;;;;qBAV5e,CAAP;mBAAyB,CAAC;iBACnB,CAAN;iBAAuB,CAAC;0BAClB,CAAd;uBAAiB,CAAC;;;;;;0BAQ8ujB,CAAC;;;;;;EADlwjB"}
1
+ {"version":3,"file":"create-session-hooks.d.ts","sourceRoot":"","sources":["../../../src/plugin/hooks/create-session-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAU7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AACrF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACrC,GAAG,EAAE,aAAa,CAAC;IACnB,YAAY,EAAE,wBAAwB,CAAC;IACvC,gBAAgB,EAAE,gBAAgB,CAAC;CACtC;;;;;;qBAmD4gD,CAAC;;;;;;;;;;;;qBAbz+C,CAAC;mBAAyB,CAAC;iBAChD,CAAX;iBAAuB,CAAC;0BAAc,CAAC;uBAAiB,CAAC;;;;;;0BAYokmB,CAAC;;;;;;EADlomB"}
@@ -1,2 +1,32 @@
1
1
  export declare function getErrorMessage(error: unknown): string;
2
+ /**
3
+ * Produce a rich, safe-to-log description of any thrown value.
4
+ *
5
+ * Motivated by SDK errors whose `.message` is empty while `.name`/`toString()`
6
+ * carry the actual signal (e.g. `NotFoundError` with no message on OpenCode
7
+ * session-delete races). Using {@link getErrorMessage} alone erases that signal.
8
+ *
9
+ * Captures:
10
+ * - `name` from the Error (defaults to `constructor.name`)
11
+ * - `message` (may be empty)
12
+ * - first few stack frames
13
+ * - `String(error)` so objects and custom toString surfaces are visible
14
+ * - Common HTTP-shape fields (`status`, `statusCode`, `code`)
15
+ * - `cause` chain summary (first level only)
16
+ *
17
+ * Returns a compact, single-line-friendly string suitable for log lines,
18
+ * plus a structured object for callers that want individual fields.
19
+ */
20
+ export interface ErrorDescription {
21
+ name: string;
22
+ message: string;
23
+ status?: string;
24
+ code?: string;
25
+ causeName?: string;
26
+ stackHead?: string;
27
+ stringForm: string;
28
+ /** Best short summary for human-readable logs. Never empty. */
29
+ brief: string;
30
+ }
31
+ export declare function describeError(error: unknown): ErrorDescription;
2
32
  //# sourceMappingURL=error-message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-message.d.ts","sourceRoot":"","sources":["../../src/shared/error-message.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD"}
1
+ {"version":3,"file":"error-message.d.ts","sourceRoot":"","sources":["../../src/shared/error-message.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC;CACjB;AAaD,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CA6D9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-magic-context",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,94 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { describeError, getErrorMessage } from "./error-message";
3
+
4
+ describe("describeError", () => {
5
+ it("extracts name and message from standard Error", () => {
6
+ const err = new TypeError("boom");
7
+ const desc = describeError(err);
8
+ expect(desc.name).toBe("TypeError");
9
+ expect(desc.message).toBe("boom");
10
+ expect(desc.brief).toContain("TypeError");
11
+ expect(desc.brief).toContain('message="boom"');
12
+ });
13
+
14
+ it("surfaces class name when message is empty (NotFoundError case)", () => {
15
+ class NotFoundError extends Error {
16
+ constructor() {
17
+ super("");
18
+ this.name = "NotFoundError";
19
+ }
20
+ }
21
+ const err = new NotFoundError();
22
+ const desc = describeError(err);
23
+ expect(desc.name).toBe("NotFoundError");
24
+ expect(desc.message).toBe("");
25
+ // brief must still carry a useful signal even with empty message
26
+ expect(desc.brief).toContain("NotFoundError");
27
+ });
28
+
29
+ it("falls back to constructor.name when .name is missing", () => {
30
+ // Simulate an SDK-shaped object where .name is an empty string
31
+ const err = Object.assign(new Error("x"), { name: "" });
32
+ const desc = describeError(err);
33
+ // Falls back to constructor.name ("Error")
34
+ expect(desc.name).toBe("Error");
35
+ });
36
+
37
+ it("extracts status/code fields from HTTP-style errors", () => {
38
+ const err = Object.assign(new Error("not found"), {
39
+ status: 404,
40
+ code: "ENOTFOUND",
41
+ });
42
+ const desc = describeError(err);
43
+ expect(desc.status).toBe("404");
44
+ expect(desc.code).toBe("ENOTFOUND");
45
+ expect(desc.brief).toContain("status=404");
46
+ expect(desc.brief).toContain("code=ENOTFOUND");
47
+ });
48
+
49
+ it("captures first stack frames in stackHead", () => {
50
+ const err = new Error("with stack");
51
+ const desc = describeError(err);
52
+ expect(desc.stackHead).toBeDefined();
53
+ expect(desc.stackHead?.length).toBeGreaterThan(0);
54
+ });
55
+
56
+ it("handles non-Error thrown values", () => {
57
+ const desc = describeError("plain string");
58
+ expect(desc.brief).toContain("plain string");
59
+ expect(desc.stringForm).toContain("plain string");
60
+ });
61
+
62
+ it("handles objects without .message", () => {
63
+ const desc = describeError({ name: "WeirdError" });
64
+ expect(desc.name).toBe("WeirdError");
65
+ expect(desc.message).toBe("");
66
+ expect(desc.brief).toContain("WeirdError");
67
+ });
68
+
69
+ it("handles undefined/null", () => {
70
+ expect(describeError(undefined).brief).toBeTruthy();
71
+ expect(describeError(null).brief).toBeTruthy();
72
+ });
73
+
74
+ it("surfaces cause name when present", () => {
75
+ const cause = new TypeError("inner");
76
+ const outer = new Error("outer");
77
+ (outer as unknown as { cause: unknown }).cause = cause;
78
+ const desc = describeError(outer);
79
+ expect(desc.causeName).toBe("TypeError");
80
+ expect(desc.brief).toContain("cause=TypeError");
81
+ });
82
+
83
+ it("clips very long messages in brief", () => {
84
+ const long = "x".repeat(1000);
85
+ const err = new Error(long);
86
+ const desc = describeError(err);
87
+ expect(desc.brief.length).toBeLessThan(500);
88
+ });
89
+
90
+ it("getErrorMessage still works as before", () => {
91
+ expect(getErrorMessage(new Error("foo"))).toBe("foo");
92
+ expect(getErrorMessage("bar")).toBe("bar");
93
+ });
94
+ });
@@ -1,3 +1,115 @@
1
1
  export function getErrorMessage(error: unknown): string {
2
2
  return error instanceof Error ? error.message : String(error);
3
3
  }
4
+
5
+ /**
6
+ * Produce a rich, safe-to-log description of any thrown value.
7
+ *
8
+ * Motivated by SDK errors whose `.message` is empty while `.name`/`toString()`
9
+ * carry the actual signal (e.g. `NotFoundError` with no message on OpenCode
10
+ * session-delete races). Using {@link getErrorMessage} alone erases that signal.
11
+ *
12
+ * Captures:
13
+ * - `name` from the Error (defaults to `constructor.name`)
14
+ * - `message` (may be empty)
15
+ * - first few stack frames
16
+ * - `String(error)` so objects and custom toString surfaces are visible
17
+ * - Common HTTP-shape fields (`status`, `statusCode`, `code`)
18
+ * - `cause` chain summary (first level only)
19
+ *
20
+ * Returns a compact, single-line-friendly string suitable for log lines,
21
+ * plus a structured object for callers that want individual fields.
22
+ */
23
+ export interface ErrorDescription {
24
+ name: string;
25
+ message: string;
26
+ status?: string;
27
+ code?: string;
28
+ causeName?: string;
29
+ stackHead?: string;
30
+ stringForm: string;
31
+ /** Best short summary for human-readable logs. Never empty. */
32
+ brief: string;
33
+ }
34
+
35
+ function readString(value: unknown): string | undefined {
36
+ if (typeof value === "string" && value.length > 0) return value;
37
+ if (typeof value === "number") return String(value);
38
+ return undefined;
39
+ }
40
+
41
+ function clip(value: string, max: number): string {
42
+ if (value.length <= max) return value;
43
+ return `${value.slice(0, max)}…`;
44
+ }
45
+
46
+ export function describeError(error: unknown): ErrorDescription {
47
+ const stringForm = clip(safeString(error), 400);
48
+
49
+ if (!(error instanceof Error) && !(error && typeof error === "object")) {
50
+ return {
51
+ name: typeof error,
52
+ message: "",
53
+ stringForm,
54
+ brief: stringForm || "<empty>",
55
+ };
56
+ }
57
+
58
+ const obj = error as Record<string, unknown>;
59
+ const nameFromField = readString(obj.name);
60
+ const nameFromCtor = error?.constructor?.name;
61
+ const name = nameFromField ?? nameFromCtor ?? "Error";
62
+
63
+ const message = readString(obj.message) ?? "";
64
+ const status = readString(obj.status) ?? readString(obj.statusCode);
65
+ const code = readString(obj.code);
66
+
67
+ let causeName: string | undefined;
68
+ const cause = obj.cause;
69
+ if (cause && typeof cause === "object") {
70
+ const causeRecord = cause as Record<string, unknown>;
71
+ causeName =
72
+ readString(causeRecord.name) ??
73
+ (cause as { constructor?: { name?: string } }).constructor?.name;
74
+ }
75
+
76
+ const stack = readString(obj.stack);
77
+ const stackHead = stack
78
+ ? stack
79
+ .split("\n")
80
+ .slice(0, 4)
81
+ .map((l) => l.trim())
82
+ .filter((l) => l.length > 0)
83
+ .join(" | ")
84
+ : undefined;
85
+
86
+ const briefParts: string[] = [];
87
+ if (name) briefParts.push(name);
88
+ if (message) briefParts.push(`message="${clip(message, 200)}"`);
89
+ if (status) briefParts.push(`status=${status}`);
90
+ if (code) briefParts.push(`code=${code}`);
91
+ if (causeName) briefParts.push(`cause=${causeName}`);
92
+ if (!message && stringForm && stringForm !== name) {
93
+ briefParts.push(`str="${clip(stringForm, 200)}"`);
94
+ }
95
+ const brief = briefParts.join(" ") || stringForm || name;
96
+
97
+ return {
98
+ name,
99
+ message,
100
+ ...(status ? { status } : {}),
101
+ ...(code ? { code } : {}),
102
+ ...(causeName ? { causeName } : {}),
103
+ ...(stackHead ? { stackHead } : {}),
104
+ stringForm,
105
+ brief,
106
+ };
107
+ }
108
+
109
+ function safeString(value: unknown): string {
110
+ try {
111
+ return String(value);
112
+ } catch {
113
+ return "<unstringifiable>";
114
+ }
115
+ }