@aexol/spectral 0.9.221 → 0.9.223

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 (166) hide show
  1. package/dist/agent/index.d.ts +20 -3
  2. package/dist/agent/index.d.ts.map +1 -1
  3. package/dist/agent/index.js +286 -80
  4. package/dist/extensions/aexol-backend-client.d.ts +85 -0
  5. package/dist/extensions/aexol-backend-client.d.ts.map +1 -0
  6. package/dist/extensions/aexol-backend-client.js +159 -0
  7. package/dist/extensions/aexol-mcp.d.ts +10 -6
  8. package/dist/extensions/aexol-mcp.d.ts.map +1 -1
  9. package/dist/extensions/aexol-mcp.js +21 -106
  10. package/dist/extensions/logic-trace-backend-draft.d.ts +68 -0
  11. package/dist/extensions/logic-trace-backend-draft.d.ts.map +1 -0
  12. package/dist/extensions/logic-trace-backend-draft.js +171 -0
  13. package/dist/extensions/logic-trace-validate.d.ts +33 -0
  14. package/dist/extensions/logic-trace-validate.d.ts.map +1 -0
  15. package/dist/extensions/logic-trace-validate.js +73 -0
  16. package/dist/extensions/logic-trace.d.ts +213 -0
  17. package/dist/extensions/logic-trace.d.ts.map +1 -0
  18. package/dist/extensions/logic-trace.js +1098 -0
  19. package/dist/generated/parser/lexer.d.ts +148 -0
  20. package/dist/generated/parser/lexer.d.ts.map +1 -0
  21. package/dist/generated/parser/lexer.js +877 -0
  22. package/dist/generated/parser/mod.d.ts +26 -0
  23. package/dist/generated/parser/mod.d.ts.map +1 -0
  24. package/dist/generated/parser/mod.js +30 -0
  25. package/dist/generated/parser/parser.d.ts +161 -0
  26. package/dist/generated/parser/parser.d.ts.map +1 -0
  27. package/dist/generated/parser/parser.js +754 -0
  28. package/dist/generated/parser/tokenizer.d.ts +107 -0
  29. package/dist/generated/parser/tokenizer.d.ts.map +1 -0
  30. package/dist/generated/parser/tokenizer.js +202 -0
  31. package/dist/generated/parser/validator.d.ts +45 -0
  32. package/dist/generated/parser/validator.d.ts.map +1 -0
  33. package/dist/generated/parser/validator.js +202 -0
  34. package/dist/generated/zeus/const.d.ts.map +1 -1
  35. package/dist/generated/zeus/const.js +4 -170
  36. package/dist/generated/zeus/index.d.ts +8 -873
  37. package/dist/generated/zeus/index.d.ts.map +1 -1
  38. package/dist/generated/zeus/index.js +0 -7
  39. package/dist/mcp/ui-stream-types.d.ts +2 -2
  40. package/dist/memory/tools/share-project-observation.js +1 -1
  41. package/dist/memory/tools/write-project-observation.d.ts.map +1 -1
  42. package/dist/memory/tools/write-project-observation.js +4 -3
  43. package/dist/sdk/agent-core/agent-loop.d.ts.map +1 -1
  44. package/dist/sdk/agent-core/agent-loop.js +2 -4
  45. package/dist/sdk/agent-core/harness/types.d.ts +0 -1
  46. package/dist/sdk/agent-core/harness/types.d.ts.map +1 -1
  47. package/dist/sdk/agent-core/index.d.ts +0 -8
  48. package/dist/sdk/agent-core/index.d.ts.map +1 -1
  49. package/dist/sdk/agent-core/index.js +0 -9
  50. package/dist/sdk/ai/index.d.ts +1 -0
  51. package/dist/sdk/ai/index.d.ts.map +1 -1
  52. package/dist/sdk/ai/index.js +1 -0
  53. package/dist/sdk/ai/utils/transient-error.d.ts +28 -0
  54. package/dist/sdk/ai/utils/transient-error.d.ts.map +1 -0
  55. package/dist/sdk/ai/utils/transient-error.js +55 -0
  56. package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
  57. package/dist/sdk/coding-agent/core/agent-session.js +7 -3
  58. package/dist/sdk/coding-agent/core/extensions/index.d.ts +1 -1
  59. package/dist/sdk/coding-agent/core/extensions/index.d.ts.map +1 -1
  60. package/dist/sdk/coding-agent/core/extensions/native-extensions.js +2 -2
  61. package/dist/sdk/coding-agent/core/extensions/types.d.ts +20 -1
  62. package/dist/sdk/coding-agent/core/extensions/types.d.ts.map +1 -1
  63. package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts +9 -4
  64. package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  65. package/dist/sdk/coding-agent/core/tools/tool-definition-wrapper.js +15 -2
  66. package/dist/sdk/coding-agent/index.d.ts +1 -1
  67. package/dist/sdk/coding-agent/index.d.ts.map +1 -1
  68. package/dist/server/agent-bridge/headless-ui.d.ts +20 -0
  69. package/dist/server/agent-bridge/headless-ui.d.ts.map +1 -0
  70. package/dist/server/agent-bridge/headless-ui.js +85 -0
  71. package/dist/server/agent-bridge/pricing.d.ts +54 -0
  72. package/dist/server/agent-bridge/pricing.d.ts.map +1 -0
  73. package/dist/server/agent-bridge/pricing.js +226 -0
  74. package/dist/server/agent-bridge/rehydrate.d.ts +35 -0
  75. package/dist/server/agent-bridge/rehydrate.d.ts.map +1 -0
  76. package/dist/server/agent-bridge/rehydrate.js +122 -0
  77. package/dist/server/agent-bridge/synthetic-model.d.ts +27 -0
  78. package/dist/server/agent-bridge/synthetic-model.d.ts.map +1 -0
  79. package/dist/server/agent-bridge/synthetic-model.js +92 -0
  80. package/dist/server/agent-bridge.d.ts +17 -1
  81. package/dist/server/agent-bridge.d.ts.map +1 -1
  82. package/dist/server/agent-bridge.js +81 -496
  83. package/dist/server/error-humanizer.d.ts +11 -3
  84. package/dist/server/error-humanizer.d.ts.map +1 -1
  85. package/dist/server/error-humanizer.js +11 -3
  86. package/dist/server/session-stream/compaction-events.d.ts +13 -0
  87. package/dist/server/session-stream/compaction-events.d.ts.map +1 -0
  88. package/dist/server/session-stream/compaction-events.js +32 -0
  89. package/dist/server/session-stream/history-prune.d.ts +35 -0
  90. package/dist/server/session-stream/history-prune.d.ts.map +1 -0
  91. package/dist/server/session-stream/history-prune.js +182 -0
  92. package/dist/server/session-stream/memory.d.ts +48 -0
  93. package/dist/server/session-stream/memory.d.ts.map +1 -0
  94. package/dist/server/session-stream/memory.js +136 -0
  95. package/dist/server/session-stream/replay-tail.d.ts +4 -0
  96. package/dist/server/session-stream/replay-tail.d.ts.map +1 -0
  97. package/dist/server/session-stream/replay-tail.js +39 -0
  98. package/dist/server/session-stream/turn-events.d.ts +33 -0
  99. package/dist/server/session-stream/turn-events.d.ts.map +1 -0
  100. package/dist/server/session-stream/turn-events.js +65 -0
  101. package/dist/server/session-stream/turn-silence.d.ts +8 -0
  102. package/dist/server/session-stream/turn-silence.d.ts.map +1 -0
  103. package/dist/server/session-stream/turn-silence.js +72 -0
  104. package/dist/server/session-stream.d.ts +137 -36
  105. package/dist/server/session-stream.d.ts.map +1 -1
  106. package/dist/server/session-stream.js +18 -509
  107. package/dist/server/storage/observation-ranking.d.ts +14 -0
  108. package/dist/server/storage/observation-ranking.d.ts.map +1 -0
  109. package/dist/server/storage/observation-ranking.js +37 -0
  110. package/dist/server/storage/preflight.d.ts +20 -0
  111. package/dist/server/storage/preflight.d.ts.map +1 -0
  112. package/dist/server/storage/preflight.js +22 -0
  113. package/dist/server/storage/project-binding.d.ts +5 -0
  114. package/dist/server/storage/project-binding.d.ts.map +1 -0
  115. package/dist/server/storage/project-binding.js +32 -0
  116. package/dist/server/storage/rows.d.ts +104 -0
  117. package/dist/server/storage/rows.d.ts.map +1 -0
  118. package/dist/server/storage/rows.js +2 -0
  119. package/dist/server/storage/schema.d.ts +20 -0
  120. package/dist/server/storage/schema.d.ts.map +1 -0
  121. package/dist/server/storage/schema.js +134 -0
  122. package/dist/server/storage/types.d.ts +66 -0
  123. package/dist/server/storage/types.d.ts.map +1 -0
  124. package/dist/server/storage/types.js +1 -0
  125. package/dist/server/storage.d.ts +7 -94
  126. package/dist/server/storage.d.ts.map +1 -1
  127. package/dist/server/storage.js +6 -224
  128. package/dist/server/wire.d.ts +19 -0
  129. package/dist/server/wire.d.ts.map +1 -1
  130. package/package.json +2 -1
  131. package/dist/sdk/agent-core/harness/agent-harness.d.ts +0 -92
  132. package/dist/sdk/agent-core/harness/agent-harness.d.ts.map +0 -1
  133. package/dist/sdk/agent-core/harness/agent-harness.js +0 -899
  134. package/dist/sdk/agent-core/harness/env/nodejs.d.ts +0 -51
  135. package/dist/sdk/agent-core/harness/env/nodejs.d.ts.map +0 -1
  136. package/dist/sdk/agent-core/harness/env/nodejs.js +0 -485
  137. package/dist/sdk/agent-core/harness/prompt-templates.d.ts +0 -48
  138. package/dist/sdk/agent-core/harness/prompt-templates.d.ts.map +0 -1
  139. package/dist/sdk/agent-core/harness/prompt-templates.js +0 -229
  140. package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts +0 -26
  141. package/dist/sdk/agent-core/harness/session/jsonl-repo.d.ts.map +0 -1
  142. package/dist/sdk/agent-core/harness/session/jsonl-repo.js +0 -100
  143. package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts +0 -33
  144. package/dist/sdk/agent-core/harness/session/jsonl-storage.d.ts.map +0 -1
  145. package/dist/sdk/agent-core/harness/session/jsonl-storage.js +0 -230
  146. package/dist/sdk/agent-core/harness/session/memory-repo.d.ts +0 -18
  147. package/dist/sdk/agent-core/harness/session/memory-repo.d.ts.map +0 -1
  148. package/dist/sdk/agent-core/harness/session/memory-repo.js +0 -41
  149. package/dist/sdk/agent-core/harness/session/memory-storage.d.ts +0 -25
  150. package/dist/sdk/agent-core/harness/session/memory-storage.d.ts.map +0 -1
  151. package/dist/sdk/agent-core/harness/session/memory-storage.js +0 -113
  152. package/dist/sdk/agent-core/harness/session/repo-utils.d.ts +0 -11
  153. package/dist/sdk/agent-core/harness/session/repo-utils.d.ts.map +0 -1
  154. package/dist/sdk/agent-core/harness/session/repo-utils.js +0 -38
  155. package/dist/sdk/agent-core/harness/skills.d.ts +0 -44
  156. package/dist/sdk/agent-core/harness/skills.d.ts.map +0 -1
  157. package/dist/sdk/agent-core/harness/skills.js +0 -310
  158. package/dist/sdk/agent-core/harness/utils/shell-output.d.ts +0 -14
  159. package/dist/sdk/agent-core/harness/utils/shell-output.d.ts.map +0 -1
  160. package/dist/sdk/agent-core/harness/utils/shell-output.js +0 -125
  161. package/dist/sdk/agent-core/node.d.ts +0 -3
  162. package/dist/sdk/agent-core/node.d.ts.map +0 -1
  163. package/dist/sdk/agent-core/node.js +0 -2
  164. package/dist/sdk/agent-core/proxy.d.ts +0 -69
  165. package/dist/sdk/agent-core/proxy.d.ts.map +0 -1
  166. package/dist/sdk/agent-core/proxy.js +0 -297
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Shared Aexol-backend MCP client bootstrap for native extensions.
3
+ *
4
+ * Extracted from `aexol-mcp.ts` (behavior-preserving: identical config/
5
+ * token resolution, identical tools/list bootstrap and OAuth re-auth flow,
6
+ * identical diagnostic strings) so other extensions that talk to the same
7
+ * JSON-RPC MCP backend can reuse one chunk of logic.
8
+ *
9
+ * Responsibilities:
10
+ * 1. Reads ~/.spectral/config.json (written by `spectral login`).
11
+ * 2. Resolves the effective API URL: SPECTRAL_MCP_URL > stored > default.
12
+ * 3. Resolves the auth token: teamApiKey > userJwt.
13
+ * 4. Instantiates AexolMcpClient and performs the initial tools/list call.
14
+ * 5. On 401/403 from tools/list, runs the OAuth re-auth flow once and
15
+ * retries the catalog fetch with a fresh token.
16
+ *
17
+ * Failure modes are non-fatal: instead of throwing, the factory returns a
18
+ * discriminated outcome. Transient diagnostics are streamed through an
19
+ * optional `warn` sink so each caller can render them with its own log
20
+ * prefix; the terminal failure reason travels in the returned outcome.
21
+ */
22
+ import { AexolMcpClient, type McpContentItem, type McpTool } from "../mcp-client.js";
23
+ import { ensureAuthenticated } from "../auth-helper.js";
24
+ /** Sink for transient bootstrap diagnostics (line without prefix/newline). */
25
+ export type BootstrapWarn = (line: string) => void;
26
+ export interface CreateAexolBackendClientOptions {
27
+ /**
28
+ * Receives transient diagnostics (tools/list failure, re-auth progress).
29
+ * Defaults to writing `[aexol-backend-client] <line>` to stderr — extension
30
+ * callers should pass a sink with their own prefix.
31
+ */
32
+ warn?: BootstrapWarn;
33
+ }
34
+ /**
35
+ * Successfully-bootstrapped client handle.
36
+ *
37
+ * NOTE: if the initial tools/list hit a 401/403 and re-auth succeeded, `tools`
38
+ * comes from a retry client while `client` is still the initially-created one.
39
+ * This intentionally mirrors the original aexol-mcp.ts behavior (call-time
40
+ * tools/call kept using the boot-time client).
41
+ */
42
+ export interface AexolBackendClient {
43
+ /** Ready-to-use JSON-RPC MCP client for tools/call (and tools/list). */
44
+ client: AexolMcpClient;
45
+ /** Effective backend URL (SPECTRAL_MCP_URL > stored config > default). */
46
+ apiUrl: string;
47
+ /** Tool catalog returned by the initial tools/list call. */
48
+ tools: McpTool[];
49
+ /**
50
+ * Re-auth helper (OAuth browser flow) for later 401/403 recovery; see
51
+ * `../auth-helper.js` for semantics (no-op failure in non-UI sessions).
52
+ * The caller owns rebuilding the client from the returned fresh config.
53
+ */
54
+ ensureAuthenticated: typeof ensureAuthenticated;
55
+ /** Render backend tool result content into one model-readable string. */
56
+ renderContentToString: typeof renderContentToString;
57
+ }
58
+ export type AexolBackendBootstrapFailureReason = "no-config" | "no-token" | "catalog-failed";
59
+ export interface AexolBackendBootstrapFailure {
60
+ ok: false;
61
+ reason: AexolBackendBootstrapFailureReason;
62
+ /** Fully-formed diagnostic, ready to be prefixed and written by the caller. */
63
+ message: string;
64
+ }
65
+ export type AexolBackendBootstrap = ({
66
+ ok: true;
67
+ } & AexolBackendClient) | AexolBackendBootstrapFailure;
68
+ /**
69
+ * Render a backend tool result into a single string for ext.
70
+ *
71
+ * The Aexol backend wraps results as `{ content: [{ type: "json", json: ... }] }`
72
+ * but may also send `{ type: "text", text: ... }`. We pick the first content
73
+ * item, prefer JSON when present (pretty-print so the model can read it),
74
+ * fall back to text, and last-resort stringify the whole content array.
75
+ */
76
+ export declare function renderContentToString(content: McpContentItem[]): string;
77
+ /**
78
+ * Bootstrap a connection to the Aexol backend MCP endpoint.
79
+ *
80
+ * Non-fatal by design: returns a failure outcome (never throws) when the
81
+ * config or token is missing, or when the initial tools/list call cannot be
82
+ * completed even after the OAuth re-auth attempt.
83
+ */
84
+ export declare function createAexolBackendClient(options?: CreateAexolBackendClientOptions): Promise<AexolBackendBootstrap>;
85
+ //# sourceMappingURL=aexol-backend-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aexol-backend-client.d.ts","sourceRoot":"","sources":["../../src/extensions/aexol-backend-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,cAAc,EAEd,KAAK,cAAc,EACnB,KAAK,OAAO,EACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAEnD,MAAM,WAAW,+BAA+B;IAC9C;;;;OAIG;IACH,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,wEAAwE;IACxE,MAAM,EAAE,cAAc,CAAC;IACvB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB;;;;OAIG;IACH,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,yEAAyE;IACzE,qBAAqB,EAAE,OAAO,qBAAqB,CAAC;CACrD;AAED,MAAM,MAAM,kCAAkC,GAC1C,WAAW,GACX,UAAU,GACV,gBAAgB,CAAC;AAErB,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,kCAAkC,CAAC;IAC3C,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,qBAAqB,GAC7B,CAAC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG,kBAAkB,CAAC,GACnC,4BAA4B,CAAC;AAEjC;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CA+BvE;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,OAAO,CAAC,qBAAqB,CAAC,CAmFhC"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Shared Aexol-backend MCP client bootstrap for native extensions.
3
+ *
4
+ * Extracted from `aexol-mcp.ts` (behavior-preserving: identical config/
5
+ * token resolution, identical tools/list bootstrap and OAuth re-auth flow,
6
+ * identical diagnostic strings) so other extensions that talk to the same
7
+ * JSON-RPC MCP backend can reuse one chunk of logic.
8
+ *
9
+ * Responsibilities:
10
+ * 1. Reads ~/.spectral/config.json (written by `spectral login`).
11
+ * 2. Resolves the effective API URL: SPECTRAL_MCP_URL > stored > default.
12
+ * 3. Resolves the auth token: teamApiKey > userJwt.
13
+ * 4. Instantiates AexolMcpClient and performs the initial tools/list call.
14
+ * 5. On 401/403 from tools/list, runs the OAuth re-auth flow once and
15
+ * retries the catalog fetch with a fresh token.
16
+ *
17
+ * Failure modes are non-fatal: instead of throwing, the factory returns a
18
+ * discriminated outcome. Transient diagnostics are streamed through an
19
+ * optional `warn` sink so each caller can render them with its own log
20
+ * prefix; the terminal failure reason travels in the returned outcome.
21
+ */
22
+ import { AexolMcpClient, AexolMcpError, } from "../mcp-client.js";
23
+ import { getApiUrl, getConfigFile, readConfig } from "../config.js";
24
+ import { ensureAuthenticated } from "../auth-helper.js";
25
+ /**
26
+ * Render a backend tool result into a single string for ext.
27
+ *
28
+ * The Aexol backend wraps results as `{ content: [{ type: "json", json: ... }] }`
29
+ * but may also send `{ type: "text", text: ... }`. We pick the first content
30
+ * item, prefer JSON when present (pretty-print so the model can read it),
31
+ * fall back to text, and last-resort stringify the whole content array.
32
+ */
33
+ export function renderContentToString(content) {
34
+ if (content.length === 0)
35
+ return "";
36
+ const first = content[0];
37
+ if (first.json !== undefined) {
38
+ try {
39
+ return JSON.stringify(first.json, null, 2);
40
+ }
41
+ catch {
42
+ return String(first.json);
43
+ }
44
+ }
45
+ if (typeof first.text === "string")
46
+ return first.text;
47
+ // For image/audio/binary content types, return a metadata placeholder
48
+ // instead of serializing the raw base64 data into the model context.
49
+ if (first.type === "image" || first.type === "audio") {
50
+ const mimeType = typeof first.mimeType === "string" ? first.mimeType : first.type;
51
+ const dataLen = typeof first.data === "string" ? first.data.length : 0;
52
+ return `[${first.type}: ${mimeType}${dataLen ? `, ${dataLen.toLocaleString()} bytes base64` : ""}]`;
53
+ }
54
+ try {
55
+ // Strip large binary fields from serialization to prevent context overflow.
56
+ const safe = content.map((c) => {
57
+ const copy = { ...c };
58
+ if ("data" in copy && typeof copy.data === "string" && copy.data.length > 200) {
59
+ copy.data = `[${copy.data.length.toLocaleString()} bytes base64 omitted]`;
60
+ }
61
+ return copy;
62
+ });
63
+ return JSON.stringify(safe, null, 2);
64
+ }
65
+ catch {
66
+ return String(content);
67
+ }
68
+ }
69
+ /**
70
+ * Bootstrap a connection to the Aexol backend MCP endpoint.
71
+ *
72
+ * Non-fatal by design: returns a failure outcome (never throws) when the
73
+ * config or token is missing, or when the initial tools/list call cannot be
74
+ * completed even after the OAuth re-auth attempt.
75
+ */
76
+ export async function createAexolBackendClient(options = {}) {
77
+ const warn = options.warn ??
78
+ ((line) => process.stderr.write(`[aexol-backend-client] ${line}\n`));
79
+ const cfg = await readConfig();
80
+ if (!cfg) {
81
+ // Pre-flight in cli.ts should have caught this. Returning a diagnostic is
82
+ // enough; the caller decides how (or whether) to surface it.
83
+ return {
84
+ ok: false,
85
+ reason: "no-config",
86
+ message: `No config found at ${getConfigFile()}; Aexol tools disabled. Run \`spectral login\`.`,
87
+ };
88
+ }
89
+ const apiUrl = getApiUrl(cfg.apiUrl);
90
+ const token = cfg.teamApiKey || cfg.userJwt || "";
91
+ if (!token) {
92
+ return {
93
+ ok: false,
94
+ reason: "no-token",
95
+ message: `No auth token in ${getConfigFile()}; Aexol tools disabled. Run \`spectral login\`.`,
96
+ };
97
+ }
98
+ const client = new AexolMcpClient(apiUrl, token);
99
+ let tools;
100
+ try {
101
+ tools = await client.listTools();
102
+ }
103
+ catch (err) {
104
+ const msg = err instanceof AexolMcpError || err instanceof Error ? err.message : String(err);
105
+ warn(`Failed to fetch tool catalog from ${apiUrl}: ${msg}`);
106
+ // Attempt auto-reauth via OAuth browser flow on auth errors
107
+ if (err instanceof AexolMcpError && (err.status === 401 || err.status === 403)) {
108
+ const authResult = await ensureAuthenticated();
109
+ if (authResult.ok && authResult.config) {
110
+ const newToken = authResult.config.teamApiKey || authResult.config.userJwt || "";
111
+ if (newToken && newToken !== token) {
112
+ warn("Re-authenticated — retrying tool catalog fetch...");
113
+ const newClient = new AexolMcpClient(apiUrl, newToken);
114
+ try {
115
+ tools = await newClient.listTools();
116
+ }
117
+ catch (retryErr) {
118
+ const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
119
+ warn(`Retry also failed: ${retryMsg}`);
120
+ return {
121
+ ok: false,
122
+ reason: "catalog-failed",
123
+ message: "Continuing without Aexol tools.",
124
+ };
125
+ }
126
+ }
127
+ else {
128
+ return {
129
+ ok: false,
130
+ reason: "catalog-failed",
131
+ message: "Re-authentication did not produce a new token. Continuing without Aexol tools.",
132
+ };
133
+ }
134
+ }
135
+ else {
136
+ return {
137
+ ok: false,
138
+ reason: "catalog-failed",
139
+ message: `Re-authentication failed: ${authResult.reason ?? "unknown"}. Continuing without Aexol tools.`,
140
+ };
141
+ }
142
+ }
143
+ else {
144
+ return {
145
+ ok: false,
146
+ reason: "catalog-failed",
147
+ message: "Continuing without Aexol tools.",
148
+ };
149
+ }
150
+ }
151
+ return {
152
+ ok: true,
153
+ client,
154
+ apiUrl,
155
+ tools,
156
+ ensureAuthenticated,
157
+ renderContentToString,
158
+ };
159
+ }
@@ -2,13 +2,17 @@
2
2
  * Aexol MCP spectral extension.
3
3
  *
4
4
  * Loaded by the spectral wrapper via `--extension <abs-path>`. On startup it:
5
- * 1. Reads ~/.spectral/config.json (writen by `spectral login`).
6
- * 2. Calls tools/list against the MCP backend once.
7
- * 3. Reads the local Studio binding (`.aexol/aexol.jsonc`) so remote tools
8
- * auto-inherit `teamId` / `projectId` without needing `remote_set_context`.
9
- * 4. Registers every returned remote tool through ext.registerTool() so the
5
+ * 1. Boots the Aexol backend MCP client via the shared
6
+ * `createAexolBackendClient()` factory in ./aexol-backend-client.ts
7
+ * (config load, token resolution, initial tools/list call, OAuth
8
+ * re-auth on 401/403).
9
+ * 2. Reads the local Studio binding (`.aexol/aexol.jsonc`) so remote tools
10
+ * auto-inherit `teamId` / `projectId`; the binding itself is managed from
11
+ * agent via the `local_bind_project` / `local_unbind_project` tools
12
+ * registered at startup (see step 4).
13
+ * 3. Registers every returned remote tool through ext.registerTool() so the
10
14
  * model can call them like built-in tools.
11
- * 5. Registers local binding-management tools (`local_bind_project`,
15
+ * 4. Registers local binding-management tools (`local_bind_project`,
12
16
  * `local_unbind_project`, `local_get_binding`) so users can bind a
13
17
  * directory to an Aexol Studio project from within the agent.
14
18
  *
@@ -1 +1 @@
1
- {"version":3,"file":"aexol-mcp.d.ts","sourceRoot":"","sources":["../../src/extensions/aexol-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoC,MAAM,8BAA8B,CAAC;AAiKnG,wBAA8B,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAsShF"}
1
+ {"version":3,"file":"aexol-mcp.d.ts","sourceRoot":"","sources":["../../src/extensions/aexol-mcp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAoC,MAAM,8BAA8B,CAAC;AAqHnG,wBAA8B,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA0PhF"}
@@ -2,13 +2,17 @@
2
2
  * Aexol MCP spectral extension.
3
3
  *
4
4
  * Loaded by the spectral wrapper via `--extension <abs-path>`. On startup it:
5
- * 1. Reads ~/.spectral/config.json (writen by `spectral login`).
6
- * 2. Calls tools/list against the MCP backend once.
7
- * 3. Reads the local Studio binding (`.aexol/aexol.jsonc`) so remote tools
8
- * auto-inherit `teamId` / `projectId` without needing `remote_set_context`.
9
- * 4. Registers every returned remote tool through ext.registerTool() so the
5
+ * 1. Boots the Aexol backend MCP client via the shared
6
+ * `createAexolBackendClient()` factory in ./aexol-backend-client.ts
7
+ * (config load, token resolution, initial tools/list call, OAuth
8
+ * re-auth on 401/403).
9
+ * 2. Reads the local Studio binding (`.aexol/aexol.jsonc`) so remote tools
10
+ * auto-inherit `teamId` / `projectId`; the binding itself is managed from
11
+ * agent via the `local_bind_project` / `local_unbind_project` tools
12
+ * registered at startup (see step 4).
13
+ * 3. Registers every returned remote tool through ext.registerTool() so the
10
14
  * model can call them like built-in tools.
11
- * 5. Registers local binding-management tools (`local_bind_project`,
15
+ * 4. Registers local binding-management tools (`local_bind_project`,
12
16
  * `local_unbind_project`, `local_get_binding`) so users can bind a
13
17
  * directory to an Aexol Studio project from within the agent.
14
18
  *
@@ -20,55 +24,10 @@
20
24
  * backend is unreachable we log a warning and return, leaving spectral to start
21
25
  * without Aexol tools rather than crashing the whole agent.
22
26
  */
23
- import { getApiUrl, getConfigFile, readConfig } from "../config.js";
24
- import { AexolMcpClient, AexolMcpError } from "../mcp-client.js";
27
+ import { createAexolBackendClient, renderContentToString } from "./aexol-backend-client.js";
25
28
  import { stat } from "node:fs/promises";
26
29
  import { join } from "node:path";
27
30
  import { readStudioBinding, writeStudioBinding, deleteStudioBinding, } from "../studio-binding.js";
28
- /**
29
- * Render a backend tool result into a single string for ext.
30
- *
31
- * The Aexol backend wraps results as `{ content: [{ type: "json", json: ... }] }`
32
- * but may also send `{ type: "text", text: ... }`. We pick the first content
33
- * item, prefer JSON when present (pretty-print so the model can read it),
34
- * fall back to text, and last-resort stringify the whole content array.
35
- */
36
- function renderContentToString(content) {
37
- if (content.length === 0)
38
- return "";
39
- const first = content[0];
40
- if (first.json !== undefined) {
41
- try {
42
- return JSON.stringify(first.json, null, 2);
43
- }
44
- catch {
45
- return String(first.json);
46
- }
47
- }
48
- if (typeof first.text === "string")
49
- return first.text;
50
- // For image/audio/binary content types, return a metadata placeholder
51
- // instead of serializing the raw base64 data into the model context.
52
- if (first.type === "image" || first.type === "audio") {
53
- const mimeType = typeof first.mimeType === "string" ? first.mimeType : first.type;
54
- const dataLen = typeof first.data === "string" ? first.data.length : 0;
55
- return `[${first.type}: ${mimeType}${dataLen ? `, ${dataLen.toLocaleString()} bytes base64` : ""}]`;
56
- }
57
- try {
58
- // Strip large binary fields from serialization to prevent context overflow.
59
- const safe = content.map((c) => {
60
- const copy = { ...c };
61
- if ("data" in copy && typeof copy.data === "string" && copy.data.length > 200) {
62
- copy.data = `[${copy.data.length.toLocaleString()} bytes base64 omitted]`;
63
- }
64
- return copy;
65
- });
66
- return JSON.stringify(safe, null, 2);
67
- }
68
- catch {
69
- return String(content);
70
- }
71
- }
72
31
  /** Build a one-line human label from a tool name like "remote_cloud_search". */
73
32
  function toLabel(name) {
74
33
  return name
@@ -127,8 +86,6 @@ async function refreshBindingForCwd(cwd) {
127
86
  */
128
87
  const MODEL_INJECTING_TOOLS = new Set([
129
88
  "remote_start_from",
130
- "remote_start_inference",
131
- "remote_refine_inference_artifact",
132
89
  "remote_refine_from_task",
133
90
  "remote_refine_aexol_content",
134
91
  ]);
@@ -163,19 +120,18 @@ function augmentParams(toolName, params, ctx, binding) {
163
120
  return { params: augmented };
164
121
  }
165
122
  export default async function aexolMcpExtension(ext) {
166
- const cfg = await readConfig();
167
- if (!cfg) {
168
- // Pre-flight in cli.ts should have caught this. Logging is enough.
169
- process.stderr.write(`[aexol-mcp] No config found at ${getConfigFile()}; Aexol tools disabled. Run \`spectral login\`.\n`);
170
- return;
171
- }
172
- const apiUrl = getApiUrl(cfg.apiUrl);
173
- const token = cfg.teamApiKey || cfg.userJwt || "";
174
- if (!token) {
175
- process.stderr.write(`[aexol-mcp] No auth token in ${getConfigFile()}; Aexol tools disabled. Run \`spectral login\`.\n`);
123
+ // Shared bootstrap (config load, token resolution, client + initial
124
+ // tools/list call, OAuth re-auth on 401/403) lives in
125
+ // ./aexol-backend-client.ts. Diagnostics stream through this sink so the
126
+ // extension's stderr output is unchanged.
127
+ const boot = await createAexolBackendClient({
128
+ warn: (line) => process.stderr.write(`[aexol-mcp] ${line}\n`),
129
+ });
130
+ if (!boot.ok) {
131
+ process.stderr.write(`[aexol-mcp] ${boot.message}\n`);
176
132
  return;
177
133
  }
178
- const client = new AexolMcpClient(apiUrl, token);
134
+ const { client, apiUrl, tools } = boot;
179
135
  // ---- Load local Studio binding on session start --------------------------
180
136
  ext.on("session_start", async (_event, ctx) => {
181
137
  try {
@@ -193,47 +149,6 @@ export default async function aexolMcpExtension(ext) {
193
149
  // Non-fatal — remote tools still work with explicit teamId/projectId.
194
150
  }
195
151
  });
196
- let tools;
197
- try {
198
- tools = await client.listTools();
199
- }
200
- catch (err) {
201
- const msg = err instanceof AexolMcpError || err instanceof Error ? err.message : String(err);
202
- process.stderr.write(`[aexol-mcp] Failed to fetch tool catalog from ${apiUrl}: ${msg}\n`);
203
- // Attempt auto-reauth via OAuth browser flow on auth errors
204
- if (err instanceof AexolMcpError && (err.status === 401 || err.status === 403)) {
205
- const { ensureAuthenticated } = await import("../auth-helper.js");
206
- const authResult = await ensureAuthenticated();
207
- if (authResult.ok && authResult.config) {
208
- const newToken = authResult.config.teamApiKey || authResult.config.userJwt || "";
209
- if (newToken && newToken !== token) {
210
- process.stderr.write("[aexol-mcp] Re-authenticated — retrying tool catalog fetch...\n");
211
- const newClient = new AexolMcpClient(apiUrl, newToken);
212
- try {
213
- tools = await newClient.listTools();
214
- }
215
- catch (retryErr) {
216
- const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
217
- process.stderr.write(`[aexol-mcp] Retry also failed: ${retryMsg}\n`);
218
- process.stderr.write("[aexol-mcp] Continuing without Aexol tools.\n");
219
- return;
220
- }
221
- }
222
- else {
223
- process.stderr.write("[aexol-mcp] Re-authentication did not produce a new token. Continuing without Aexol tools.\n");
224
- return;
225
- }
226
- }
227
- else {
228
- process.stderr.write(`[aexol-mcp] Re-authentication failed: ${authResult.reason ?? "unknown"}. Continuing without Aexol tools.\n`);
229
- return;
230
- }
231
- }
232
- else {
233
- process.stderr.write("[aexol-mcp] Continuing without Aexol tools.\n");
234
- return;
235
- }
236
- }
237
152
  let registered = 0;
238
153
  for (const tool of tools) {
239
154
  // Defensive defaults: an empty-object schema is valid JSON Schema and
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Backend refine draft adapter for the logic-trace extension.
3
+ *
4
+ * Implements the `LogicTraceDraftRunner` seam (see logic-trace.ts) on top of
5
+ * the backend from/refine pipeline instead of the local LLM agent loop:
6
+ * 1. `remote_refine_aexol_content` — submits the CURRENT remote logic.aexol
7
+ * content plus a compressed merge instruction prompt (full-spec rewrite,
8
+ * "merge, never append-only", grammar + leak rules) and the serialized
9
+ * recent-activity delta. Answers `{ ok, task: { id, status } }`. The
10
+ * session model is intentionally NOT sent — the backend resolves the
11
+ * team's default model.
12
+ * 2. `remote_get_from_task` — polled locally (NOT via remote_wait_task,
13
+ * whose internal wait exceeds the MCP client's 30s fetch abort) until a
14
+ * terminal status or the configured timeout. Answers
15
+ * `{ ok, task: { id, status, aexolCode, … } }`.
16
+ * 3. COMPLETED with an `aexolCode` → `{ submitted: { content, changed } }`
17
+ * where `changed` is a plain string comparison against the current
18
+ * remote content.
19
+ * 4. Anything else (FAILED / CANCELLED / poll timeout / missing content /
20
+ * any error) → `{}` — the empty outcome is the seam's fallback signal
21
+ * and attemptLogicTraceSync retries with the local draft runner. The
22
+ * adapter NEVER throws past the seam.
23
+ *
24
+ * This file only adapts the draft STEP; the downstream sanitize → parser
25
+ * gate → hash dedupe → push flow stays in logic-trace.ts untouched.
26
+ */
27
+ import { AexolMcpClient } from "../mcp-client.js";
28
+ import type { LogicTraceDraftRunner } from "./logic-trace.js";
29
+ /** Default cadence between `remote_get_from_task` polls. */
30
+ export declare const DEFAULT_DRAFT_BACKEND_POLL_INTERVAL_MS = 2000;
31
+ /** Default total wait for a refine task to reach a terminal status. */
32
+ export declare const DEFAULT_DRAFT_BACKEND_POLL_TIMEOUT_MS = 120000;
33
+ export interface BackendRefineDraftRunnerOptions {
34
+ /** Bootstrapped Aexol backend MCP client (from the extension's bootstrap). */
35
+ client: AexolMcpClient;
36
+ /** Bound Studio project id (every remote_* call carries it). */
37
+ projectId: string;
38
+ /**
39
+ * Bound team id — injected on every refine/get call so team access
40
+ * resolves to the BOUND project's team, not the token's first membership.
41
+ */
42
+ teamId?: string;
43
+ /** Poll cadence for `remote_get_from_task` (default 2s). */
44
+ pollIntervalMs?: number;
45
+ /** Total wait for a terminal task status (default 120s). */
46
+ pollTimeoutMs?: number;
47
+ /** Optional retry hint forwarded to `remote_refine_aexol_content`. */
48
+ maxRetries?: number;
49
+ /** Optional guidance forwarded to `remote_refine_aexol_content`. */
50
+ guidance?: string;
51
+ /**
52
+ * Constraint block (grammar + leak rules) merged into the refinement
53
+ * prompt. Supplied by the caller so the adapter stays decoupled from the
54
+ * logic-trace grammar constant. Only the shared leak-rule list is imported
55
+ * from logic-trace.ts directly, and it is read lazily inside
56
+ * buildRefinementPrompt (never at module-eval time) to stay safe within
57
+ * the logic-trace ↔ backend-draft import cycle.
58
+ */
59
+ promptRules?: string;
60
+ /** Injectable sleep (tests). */
61
+ sleep?: (ms: number) => Promise<void>;
62
+ }
63
+ /**
64
+ * Build a `LogicTraceDraftRunner` that delegates the .aexol draft step to the
65
+ * backend from/refine pipeline. See the module doc for the outcome contract.
66
+ */
67
+ export declare function createBackendRefineDraftRunner(options: BackendRefineDraftRunnerOptions): LogicTraceDraftRunner;
68
+ //# sourceMappingURL=logic-trace-backend-draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logic-trace-backend-draft.d.ts","sourceRoot":"","sources":["../../src/extensions/logic-trace-backend-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,cAAc,EAAuB,MAAM,kBAAkB,CAAC;AAEvE,OAAO,KAAK,EAGX,qBAAqB,EACrB,MAAM,kBAAkB,CAAC;AAI1B,4DAA4D;AAC5D,eAAO,MAAM,sCAAsC,OAAQ,CAAC;AAC5D,uEAAuE;AACvE,eAAO,MAAM,qCAAqC,SAAU,CAAC;AAqB7D,MAAM,WAAW,+BAA+B;IAC/C,8EAA8E;IAC9E,MAAM,EAAE,cAAc,CAAC;IACvB,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAqBD;;;GAGG;AACH,wBAAgB,8BAA8B,CAC7C,OAAO,EAAE,+BAA+B,GACtC,qBAAqB,CAoGvB"}