@leanandmean/coding-agent 0.74.1-scramjet.33 → 0.74.1-scramjet.35

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.
@@ -1,5 +1,7 @@
1
- // SCRAMJET-DIVERGENCE: Removed pi.dev network code (LATEST_VERSION_URL, fetch calls);
2
- // renamed Pi-prefixed identifiers; network functions gutted to return undefined.
1
+ // SCRAMJET-DIVERGENCE: Replaced pi.dev release lookup with validated npm package resolution.
2
+ import { execCommand } from "../core/exec.js";
3
+ import { shouldUseWindowsShell } from "./child-process.js";
4
+ export const CURRENT_RELEASE_TIMEOUT_MS = 5000;
3
5
  function parsePackageVersion(version) {
4
6
  const match = version
5
7
  .trim()
@@ -64,7 +66,47 @@ function comparePackageVersions(leftVersion, rightVersion) {
64
66
  export function isNewerPackageVersion(candidateVersion, currentVersion) {
65
67
  return (comparePackageVersions(candidateVersion, currentVersion) ?? 0) > 0;
66
68
  }
67
- export async function getLatestRelease(_currentVersion, _options = {}) {
68
- return undefined;
69
+ export const executeCurrentReleaseLookup = async (command, args, options) => {
70
+ const result = await execCommand(command, args, process.cwd(), {
71
+ timeout: options.timeout,
72
+ shell: shouldUseWindowsShell(command),
73
+ });
74
+ return {
75
+ stdout: result.stdout,
76
+ stderr: result.stderr || result.spawnError?.message || "",
77
+ code: result.code,
78
+ killed: result.killed,
79
+ stdoutError: result.stdoutError,
80
+ stderrError: result.stderrError,
81
+ };
82
+ };
83
+ export async function resolveCurrentRelease(packageName, executor = executeCurrentReleaseLookup, timeoutMs = CURRENT_RELEASE_TIMEOUT_MS, commandPrefix = ["npm"]) {
84
+ const [command, ...prefixArgs] = commandPrefix;
85
+ if (!command)
86
+ throw new Error("npm release lookup requires a command");
87
+ const result = await executor(command, [...prefixArgs, "view", packageName, "version", "--json"], {
88
+ timeout: timeoutMs,
89
+ });
90
+ if (result.stdoutError)
91
+ throw new Error(`npm release lookup stdout failed: ${result.stdoutError.message}`);
92
+ if (result.stderrError)
93
+ throw new Error(`npm release lookup stderr failed: ${result.stderrError.message}`);
94
+ if (result.killed)
95
+ throw new Error(`npm release lookup timed out after ${timeoutMs}ms`);
96
+ if (result.code !== 0) {
97
+ const detail = result.stderr.trim();
98
+ throw new Error(`npm release lookup failed with exit code ${result.code}${detail ? `: ${detail}` : ""}`);
99
+ }
100
+ let version;
101
+ try {
102
+ version = JSON.parse(result.stdout);
103
+ }
104
+ catch {
105
+ throw new Error("npm release lookup returned malformed JSON");
106
+ }
107
+ if (typeof version !== "string" || !parsePackageVersion(version)) {
108
+ throw new Error("npm release lookup returned an invalid package version");
109
+ }
110
+ return { packageName, version };
69
111
  }
70
112
  //# sourceMappingURL=version-check.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["// SCRAMJET-DIVERGENCE: Removed pi.dev network code (LATEST_VERSION_URL, fetch calls);\n// renamed Pi-prefixed identifiers; network functions gutted to return undefined.\n\nexport interface LatestRelease {\n\tversion: string;\n\tpackageName?: string;\n}\n\ninterface ParsedVersion {\n\tmajor: string;\n\tminor: string;\n\tpatch: string;\n\tprerelease?: string;\n}\n\nfunction parsePackageVersion(version: string): ParsedVersion | undefined {\n\tconst match = version\n\t\t.trim()\n\t\t.match(\n\t\t\t/^v?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/,\n\t\t);\n\tif (!match || match[4]?.split(\".\").some((identifier) => /^0\\d+$/.test(identifier))) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tmajor: match[1],\n\t\tminor: match[2],\n\t\tpatch: match[3],\n\t\tprerelease: match[4],\n\t};\n}\n\nfunction comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = parsePackageVersion(leftVersion);\n\tconst right = parsePackageVersion(rightVersion);\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\n\tfor (const component of [\"major\", \"minor\", \"patch\"] as const) {\n\t\tif (left[component] === right[component]) continue;\n\t\tif (left[component].length !== right[component].length) {\n\t\t\treturn left[component].length - right[component].length;\n\t\t}\n\t\treturn left[component].localeCompare(right[component]);\n\t}\n\tif (left.prerelease === right.prerelease) return 0;\n\tif (!left.prerelease) return 1;\n\tif (!right.prerelease) return -1;\n\tconst leftIdentifiers = left.prerelease.split(\".\");\n\tconst rightIdentifiers = right.prerelease.split(\".\");\n\tfor (let index = 0; index < Math.max(leftIdentifiers.length, rightIdentifiers.length); index++) {\n\t\tconst leftIdentifier = leftIdentifiers[index];\n\t\tconst rightIdentifier = rightIdentifiers[index];\n\t\tif (leftIdentifier === undefined) return -1;\n\t\tif (rightIdentifier === undefined) return 1;\n\t\tif (leftIdentifier === rightIdentifier) continue;\n\t\tconst leftNumeric = /^\\d+$/.test(leftIdentifier);\n\t\tconst rightNumeric = /^\\d+$/.test(rightIdentifier);\n\t\tif (leftNumeric && rightNumeric) {\n\t\t\tif (leftIdentifier.length !== rightIdentifier.length) return leftIdentifier.length - rightIdentifier.length;\n\t\t\treturn leftIdentifier.localeCompare(rightIdentifier);\n\t\t}\n\t\tif (leftNumeric) return -1;\n\t\tif (rightNumeric) return 1;\n\t\treturn leftIdentifier.localeCompare(rightIdentifier);\n\t}\n\treturn 0;\n}\n\n// SCRAMJET-DIVERGENCE: malformed package versions fail closed for passive update notifications (#432).\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\treturn (comparePackageVersions(candidateVersion, currentVersion) ?? 0) > 0;\n}\n\nexport async function getLatestRelease(\n\t_currentVersion: string,\n\t_options: { timeoutMs?: number } = {},\n): Promise<LatestRelease | undefined> {\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["// SCRAMJET-DIVERGENCE: Replaced pi.dev release lookup with validated npm package resolution.\n\nimport { type ExecError, execCommand } from \"../core/exec.js\";\nimport { shouldUseWindowsShell } from \"./child-process.js\";\n\nexport interface CurrentRelease {\n\tpackageName: string;\n\tversion: string;\n}\n\nexport interface CurrentReleaseExecution {\n\tstdout: string;\n\tstderr: string;\n\tcode: number;\n\tkilled: boolean;\n\tstdoutError?: ExecError;\n\tstderrError?: ExecError;\n}\n\nexport type CurrentReleaseExecutor = (\n\tcommand: string,\n\targs: string[],\n\toptions: { timeout: number },\n) => Promise<CurrentReleaseExecution>;\n\nexport const CURRENT_RELEASE_TIMEOUT_MS = 5000;\n\ninterface ParsedVersion {\n\tmajor: string;\n\tminor: string;\n\tpatch: string;\n\tprerelease?: string;\n}\n\nfunction parsePackageVersion(version: string): ParsedVersion | undefined {\n\tconst match = version\n\t\t.trim()\n\t\t.match(\n\t\t\t/^v?(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/,\n\t\t);\n\tif (!match || match[4]?.split(\".\").some((identifier) => /^0\\d+$/.test(identifier))) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tmajor: match[1],\n\t\tminor: match[2],\n\t\tpatch: match[3],\n\t\tprerelease: match[4],\n\t};\n}\n\nfunction comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = parsePackageVersion(leftVersion);\n\tconst right = parsePackageVersion(rightVersion);\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\n\tfor (const component of [\"major\", \"minor\", \"patch\"] as const) {\n\t\tif (left[component] === right[component]) continue;\n\t\tif (left[component].length !== right[component].length) {\n\t\t\treturn left[component].length - right[component].length;\n\t\t}\n\t\treturn left[component].localeCompare(right[component]);\n\t}\n\tif (left.prerelease === right.prerelease) return 0;\n\tif (!left.prerelease) return 1;\n\tif (!right.prerelease) return -1;\n\tconst leftIdentifiers = left.prerelease.split(\".\");\n\tconst rightIdentifiers = right.prerelease.split(\".\");\n\tfor (let index = 0; index < Math.max(leftIdentifiers.length, rightIdentifiers.length); index++) {\n\t\tconst leftIdentifier = leftIdentifiers[index];\n\t\tconst rightIdentifier = rightIdentifiers[index];\n\t\tif (leftIdentifier === undefined) return -1;\n\t\tif (rightIdentifier === undefined) return 1;\n\t\tif (leftIdentifier === rightIdentifier) continue;\n\t\tconst leftNumeric = /^\\d+$/.test(leftIdentifier);\n\t\tconst rightNumeric = /^\\d+$/.test(rightIdentifier);\n\t\tif (leftNumeric && rightNumeric) {\n\t\t\tif (leftIdentifier.length !== rightIdentifier.length) return leftIdentifier.length - rightIdentifier.length;\n\t\t\treturn leftIdentifier.localeCompare(rightIdentifier);\n\t\t}\n\t\tif (leftNumeric) return -1;\n\t\tif (rightNumeric) return 1;\n\t\treturn leftIdentifier.localeCompare(rightIdentifier);\n\t}\n\treturn 0;\n}\n\n// SCRAMJET-DIVERGENCE: malformed package versions fail closed for passive update notifications (#432).\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\treturn (comparePackageVersions(candidateVersion, currentVersion) ?? 0) > 0;\n}\n\nexport const executeCurrentReleaseLookup: CurrentReleaseExecutor = async (command, args, options) => {\n\tconst result = await execCommand(command, args, process.cwd(), {\n\t\ttimeout: options.timeout,\n\t\tshell: shouldUseWindowsShell(command),\n\t});\n\treturn {\n\t\tstdout: result.stdout,\n\t\tstderr: result.stderr || result.spawnError?.message || \"\",\n\t\tcode: result.code,\n\t\tkilled: result.killed,\n\t\tstdoutError: result.stdoutError,\n\t\tstderrError: result.stderrError,\n\t};\n};\n\nexport async function resolveCurrentRelease(\n\tpackageName: string,\n\texecutor: CurrentReleaseExecutor = executeCurrentReleaseLookup,\n\ttimeoutMs = CURRENT_RELEASE_TIMEOUT_MS,\n\tcommandPrefix: string[] = [\"npm\"],\n): Promise<CurrentRelease> {\n\tconst [command, ...prefixArgs] = commandPrefix;\n\tif (!command) throw new Error(\"npm release lookup requires a command\");\n\tconst result = await executor(command, [...prefixArgs, \"view\", packageName, \"version\", \"--json\"], {\n\t\ttimeout: timeoutMs,\n\t});\n\tif (result.stdoutError) throw new Error(`npm release lookup stdout failed: ${result.stdoutError.message}`);\n\tif (result.stderrError) throw new Error(`npm release lookup stderr failed: ${result.stderrError.message}`);\n\tif (result.killed) throw new Error(`npm release lookup timed out after ${timeoutMs}ms`);\n\tif (result.code !== 0) {\n\t\tconst detail = result.stderr.trim();\n\t\tthrow new Error(`npm release lookup failed with exit code ${result.code}${detail ? `: ${detail}` : \"\"}`);\n\t}\n\n\tlet version: unknown;\n\ttry {\n\t\tversion = JSON.parse(result.stdout);\n\t} catch {\n\t\tthrow new Error(\"npm release lookup returned malformed JSON\");\n\t}\n\tif (typeof version !== \"string\" || !parsePackageVersion(version)) {\n\t\tthrow new Error(\"npm release lookup returned an invalid package version\");\n\t}\n\treturn { packageName, version };\n}\n"]}
@@ -288,8 +288,12 @@ user sends prompt ────────────────────
288
288
  │ ┌─── turn (repeats while LLM calls tools) ───┐ │
289
289
  │ │ │ │
290
290
  │ ├─► turn_start │ │
291
+ │ ├─► routing refresh │ │
291
292
  │ ├─► context (can modify messages) │ │
293
+ │ ├─► before_provider_call (can replace request-local system prompt)
294
+ │ ├─► provider serialization │ │
292
295
  │ ├─► before_provider_request (can inspect or replace payload)
296
+ │ ├─► transport │ │
293
297
  │ ├─► after_provider_response (status + headers, before stream consume)
294
298
  │ │ │ │
295
299
  │ │ LLM responds, may call tools: │ │
@@ -630,14 +634,29 @@ pi.on("context", async (event, ctx) => {
630
634
  });
631
635
  ```
632
636
 
637
+ #### before_provider_call
638
+
639
+ Fired after request routing and message-context transformation, immediately before provider serialization. `event.model` is an immutable, handler-isolated snapshot of the exact model routed for this call, and `event.systemPrompt` is its request-local provider-neutral prompt (`string` or `SystemPromptSection[]`). Return `{ systemPrompt }` to replace only this call's prompt. Replacements chain in extension load order and never accumulate in session state.
640
+
641
+ ```typescript
642
+ pi.on("before_provider_call", (event) => {
643
+ return {
644
+ systemPrompt: typeof event.systemPrompt === "string"
645
+ ? `${event.systemPrompt}\n\nRequest routed to ${event.model.provider}/${event.model.id}`
646
+ : [...event.systemPrompt, { id: "routed-model", text: `\n\nRequest routed to ${event.model.provider}/${event.model.id}` }],
647
+ };
648
+ });
649
+ ```
650
+
633
651
  #### before_provider_request
634
652
 
635
- Fired after the provider-specific payload is built, right before the request is sent. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
653
+ Fired after the provider-specific payload is built, right before transport. `event.model` is a fresh immutable snapshot of the same exact routed model exposed by `before_provider_call`; mutating one handler's snapshot cannot affect later handlers or transport routing. Handlers run in extension load order. Returning `undefined` keeps the payload unchanged. Returning any other value replaces the payload for later handlers and for the actual request.
636
654
 
637
- This hook can rewrite provider-level system instructions or remove them entirely. Those payload-level changes are not reflected by `ctx.getSystemPrompt()`, which reports Scramjet's system prompt string rather than the final serialized provider payload.
655
+ This hook can rewrite provider-level system instructions or remove them entirely. Those payload-level changes are not reflected by `ctx.getSystemPrompt()`, which reports Scramjet's stored system prompt string rather than request-local `before_provider_call` changes or the final serialized provider payload.
638
656
 
639
657
  ```typescript
640
- pi.on("before_provider_request", (event, ctx) => {
658
+ pi.on("before_provider_request", (event) => {
659
+ console.log(`${event.model.provider}/${event.model.id}`);
641
660
  console.log(JSON.stringify(event.payload, null, 2));
642
661
 
643
662
  // Optional: replace payload
@@ -645,7 +664,7 @@ pi.on("before_provider_request", (event, ctx) => {
645
664
  });
646
665
  ```
647
666
 
648
- This is mainly useful for debugging provider serialization and cache behavior.
667
+ The complete request order is routing refresh → `context` transformation → `before_provider_call` → provider serialization → `before_provider_request` → transport. This is mainly useful for request-local prompt preparation and debugging provider serialization or cache behavior.
649
668
 
650
669
  #### after_provider_response
651
670
 
@@ -995,6 +1014,7 @@ Returns Scramjet's current system prompt string.
995
1014
 
996
1015
  - During `before_agent_start`, this reflects chained system-prompt changes made so far for the current turn.
997
1016
  - It does not include later `context` message mutations.
1017
+ - It does not include request-local `before_provider_call` prompt replacements.
998
1018
  - It does not include `before_provider_request` payload rewrites.
999
1019
  - If later-loaded extensions run after yours, they can still change what is ultimately sent.
1000
1020
 
package/docs/packages.md CHANGED
@@ -38,6 +38,8 @@ scramjet update --extension npm:@foo/bar
38
38
 
39
39
  By default, `install` and `remove` write to global settings (`~/.scramjet/agent/settings.json`). Use `-l` to write to project settings (`.scramjet/settings.json`) instead. Project settings can be shared with your team, and scramjet installs any missing packages automatically on startup.
40
40
 
41
+ For a globally package-manager-managed CLI, self-update resolves one current npm release, installs that exact package version with the owning package manager, and verifies the managed package metadata before reporting success. This verification does not inspect or repair duplicate installations selected through `PATH`.
42
+
41
43
  To try a package without installing it, use `--extension` or `-e`. This installs to a temporary directory for the current run only:
42
44
 
43
45
  ```bash
@@ -81,6 +81,7 @@ interface UserMessage {
81
81
  interface AssistantMessage {
82
82
  role: "assistant";
83
83
  content: (TextContent | ThinkingContent | ToolCall)[];
84
+ origin?: "provider" | "harness"; // Explicit for new messages; absent in legacy sessions
84
85
  api: string;
85
86
  provider: string;
86
87
  model: string;
@@ -203,7 +204,7 @@ A message in the conversation. The `message` field contains an `AgentMessage`.
203
204
 
204
205
  ```json
205
206
  {"type":"message","id":"a1b2c3d4","parentId":"prev1234","timestamp":"2024-12-03T14:00:01.000Z","message":{"role":"user","content":"Hello"}}
206
- {"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
207
+ {"type":"message","id":"b2c3d4e5","parentId":"a1b2c3d4","timestamp":"2024-12-03T14:00:02.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Hi!"}],"origin":"provider","provider":"anthropic","model":"claude-sonnet-4-5","usage":{...},"stopReason":"stop"}}
207
208
  {"type":"message","id":"c3d4e5f6","parentId":"b2c3d4e5","timestamp":"2024-12-03T14:00:03.000Z","message":{"role":"toolResult","toolCallId":"call_123","toolName":"bash","content":[{"type":"text","text":"output"}],"isError":false}}
208
209
  ```
209
210
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leanandmean/coding-agent",
3
- "version": "0.74.1-scramjet.33",
3
+ "version": "0.74.1-scramjet.35",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,8 +33,8 @@
33
33
  "test": "vitest --run --passWithNoTests"
34
34
  },
35
35
  "dependencies": {
36
- "@leanandmean/agent": "0.74.1-scramjet.15",
37
- "@leanandmean/ai": "0.74.1-scramjet.11",
36
+ "@leanandmean/agent": "0.74.1-scramjet.16",
37
+ "@leanandmean/ai": "0.74.1-scramjet.12",
38
38
  "@leanandmean/tui": "0.74.1-scramjet.10",
39
39
  "@silvia-odwyer/photon-node": "^0.3.4",
40
40
  "chalk": "^5.5.0",