@mlx-node/agent 0.0.8 → 0.0.10

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 (49) hide show
  1. package/dist/catalog.d.ts +15 -0
  2. package/dist/catalog.d.ts.map +1 -1
  3. package/dist/catalog.js +15 -0
  4. package/dist/cold-tier.d.ts +99 -0
  5. package/dist/cold-tier.d.ts.map +1 -0
  6. package/dist/cold-tier.js +155 -0
  7. package/dist/extensions/local-image-input.d.ts +24 -0
  8. package/dist/extensions/local-image-input.d.ts.map +1 -0
  9. package/dist/extensions/local-image-input.js +114 -0
  10. package/dist/extensions/subagent.d.ts +20 -1
  11. package/dist/extensions/subagent.d.ts.map +1 -1
  12. package/dist/extensions/subagent.js +46 -6
  13. package/dist/paths.d.ts +13 -0
  14. package/dist/paths.d.ts.map +1 -0
  15. package/dist/paths.js +18 -0
  16. package/dist/provider/chat-config.d.ts +1 -1
  17. package/dist/provider/chat-config.d.ts.map +1 -1
  18. package/dist/provider/chat-config.js +15 -3
  19. package/dist/provider/events.d.ts +9 -0
  20. package/dist/provider/events.d.ts.map +1 -1
  21. package/dist/provider/events.js +15 -0
  22. package/dist/provider/index.d.ts +12 -1
  23. package/dist/provider/index.d.ts.map +1 -1
  24. package/dist/provider/index.js +153 -7
  25. package/dist/provider/metrics-trace.d.ts +274 -0
  26. package/dist/provider/metrics-trace.d.ts.map +1 -0
  27. package/dist/provider/metrics-trace.js +174 -0
  28. package/dist/provider/mlx-identity.d.ts +16 -0
  29. package/dist/provider/mlx-identity.d.ts.map +1 -0
  30. package/dist/provider/mlx-identity.js +15 -0
  31. package/dist/provider/model-host.d.ts +45 -3
  32. package/dist/provider/model-host.d.ts.map +1 -1
  33. package/dist/provider/model-host.js +34 -2
  34. package/dist/provider/model-registry-filter.d.ts +74 -18
  35. package/dist/provider/model-registry-filter.d.ts.map +1 -1
  36. package/dist/provider/model-registry-filter.js +229 -38
  37. package/dist/provider/models.d.ts +2 -3
  38. package/dist/provider/models.d.ts.map +1 -1
  39. package/dist/provider/models.js +46 -20
  40. package/dist/provider/stream-adapter.d.ts +37 -4
  41. package/dist/provider/stream-adapter.d.ts.map +1 -1
  42. package/dist/provider/stream-adapter.js +82 -7
  43. package/dist/provider/warm-reuse.d.ts +11 -8
  44. package/dist/provider/warm-reuse.d.ts.map +1 -1
  45. package/dist/provider/warm-reuse.js +14 -7
  46. package/dist/run-agent.d.ts +12 -3
  47. package/dist/run-agent.d.ts.map +1 -1
  48. package/dist/run-agent.js +31 -5
  49. package/package.json +10 -5
package/dist/catalog.d.ts CHANGED
@@ -23,4 +23,19 @@ export interface CatalogEntry {
23
23
  export declare const MODEL_CATALOG: readonly CatalogEntry[];
24
24
  /** Catalog entries the wizard offers (hidden entries filtered out). */
25
25
  export declare function visibleCatalog(): CatalogEntry[];
26
+ /**
27
+ * Cold-tier facts, re-exported through this subpath.
28
+ *
29
+ * `@mlx-node/agent/catalog` is the agent package's one NATIVE-FREE entry point:
30
+ * the package root re-exports `provider/index.ts`, which value-imports
31
+ * `@mlx-node/core`. The dashboard is a separate viewer process that must never
32
+ * link the addon (docs/dashboard.md: "no Metal init, instant start"), and
33
+ * `mlx agent --help` must print without loading weights — so both reach the
34
+ * cold-tier allowlist and the cache-root canonicalizer through here.
35
+ *
36
+ * These are RE-EXPORTS. The definitions live in `./cold-tier.ts` and there is
37
+ * exactly one of each; `packages/agent/__test__/cold-tier-families.test.ts`
38
+ * guards the allowlist against the native side.
39
+ */
40
+ export { COLD_TIER_RESTORE_FAMILIES, canonicalCacheRoot, coldTierRestoreFamilyList } from './cold-tier.js';
26
41
  //# sourceMappingURL=catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EA+BhD,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,YAAY,EAAE,CAE/C"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EA+BhD,CAAC;AAEF,uEAAuE;AACvE,wBAAgB,cAAc,IAAI,YAAY,EAAE,CAE/C;AAED;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/catalog.js CHANGED
@@ -42,3 +42,18 @@ export const MODEL_CATALOG = [
42
42
  export function visibleCatalog() {
43
43
  return MODEL_CATALOG.filter((entry) => !entry.hidden);
44
44
  }
45
+ /**
46
+ * Cold-tier facts, re-exported through this subpath.
47
+ *
48
+ * `@mlx-node/agent/catalog` is the agent package's one NATIVE-FREE entry point:
49
+ * the package root re-exports `provider/index.ts`, which value-imports
50
+ * `@mlx-node/core`. The dashboard is a separate viewer process that must never
51
+ * link the addon (docs/dashboard.md: "no Metal init, instant start"), and
52
+ * `mlx agent --help` must print without loading weights — so both reach the
53
+ * cold-tier allowlist and the cache-root canonicalizer through here.
54
+ *
55
+ * These are RE-EXPORTS. The definitions live in `./cold-tier.ts` and there is
56
+ * exactly one of each; `packages/agent/__test__/cold-tier-families.test.ts`
57
+ * guards the allowlist against the native side.
58
+ */
59
+ export { COLD_TIER_RESTORE_FAMILIES, canonicalCacheRoot, coldTierRestoreFamilyList } from './cold-tier.js';
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Cold-tier facts shared by the agent (writer), the CLI (help text) and the
3
+ * dashboard (reader), kept in a NATIVE-FREE leaf module.
4
+ *
5
+ * `provider/model-host.ts` value-imports `@mlx-node/lm`, which loads the native
6
+ * addon; the dashboard is a separate viewer process that must never link it
7
+ * ("no Metal init, instant start" — docs/dashboard.md), and `mlx agent --help`
8
+ * must print without loading weights. This module therefore has NO runtime
9
+ * imports beyond `node:` builtins, and reaches those consumers through the
10
+ * existing native-free `@mlx-node/agent/catalog` subpath, which re-exports it.
11
+ * There is deliberately no `./cold-tier` entry in `package.json`: a second
12
+ * published subpath would be a second thing to keep native-free, and `catalog`
13
+ * already carries that guarantee.
14
+ *
15
+ * Everything here is a single source of truth. Do not copy the family list or
16
+ * re-implement the root canonicalization at a call site; import them.
17
+ */
18
+ /**
19
+ * Model families whose paged prefix blocks can be restored from the SSD cold
20
+ * tier soundly. The single source of truth on the TypeScript side, mirroring
21
+ * `COLD_RESTORE_FAMILIES` in `crates/mlx-core/src/cold_tier.rs`; the two gate
22
+ * the same decision from opposite ends, and
23
+ * `packages/agent/__test__/cold-tier-families.test.ts` asserts they never
24
+ * drift.
25
+ *
26
+ * A family belongs here only when EVERY piece of per-token state its forward
27
+ * pass carries between turns is reconstructible from the tier — either because
28
+ * it all lives inside the paged pool, or because the part that does not is
29
+ * persisted alongside as a cold-tier sidecar and the restore reconciles down to
30
+ * a boundary that sidecar actually backs:
31
+ *
32
+ * - `qwen3` (dense) sizes its pool over all layers, so the pool holds the
33
+ * complete KV for the prefix and needs no sidecar.
34
+ * - `gemma4` sizes its pool over the full-attention layers only, but persists
35
+ * its out-of-pool sliding-window `RotatingKVCache` state as a
36
+ * `ColdGroup::SlidingWindow` sidecar, and its `ColdSidecarPolicy` makes the
37
+ * native restore walk refuse any boundary a validated sidecar does not back.
38
+ * - `qwen3_5` (dense) sizes its pool over the full-attention layers only, but
39
+ * persists its out-of-pool GDN recurrent state (conv + recurrent) as a
40
+ * `ColdGroup::GdnState` sidecar, and its `ColdSidecarPolicy` reconciles the
41
+ * native restore down to the deepest block-aligned boundary a validated
42
+ * sidecar backs (a recurrent state is valid only at its exact prefix length).
43
+ * - `qwen3_5_moe` keeps the SAME GDN recurrent state outside the pool — same
44
+ * shapes, same dtype, same layer mapping — so it shares the dense family's
45
+ * sidecar codec and `ColdSidecarPolicy` verbatim. Its restart-parity gate has
46
+ * since been run on `Qwen3.6-35b-a3b-UD-Q2_K_XL-mlx`: the restore reconciled
47
+ * onto ladder rung 304 of `[16, 64, 304, 1248]` with `hits=42` and
48
+ * `corruptions=0`, and the text matched a no-persist baseline.
49
+ * - `lfm2` / `lfm2_moe` keep short-conv state outside the pool with no
50
+ * serialization path for it at all, AND drive the uniform native adapter
51
+ * API whose restore branch is already wired to the tier — asking for
52
+ * persistence on their behalf would restore an incomplete prefix silently.
53
+ *
54
+ * Widening this set is a correctness decision, never a perf one, and it is
55
+ * authorized by exactly one thing: the family's native restart-parity gate
56
+ * passing on real weights with `hits > 0` and `corruptions == 0`.
57
+ *
58
+ * `mlx agent --no-persist-cache` applies to EVERY family in this set, not to
59
+ * one of them: it is a SINGLE process-wide boolean handed to every load whose
60
+ * family is on the allowlist (`MlxModelHost.runWithResident` →
61
+ * `resolveModelPathFn(entry, { persistPagedCache })`). Families off the
62
+ * allowlist are handed no policy at all — not because the flag spares them, but
63
+ * because `cold_tier::resolve_persist_cold` refuses to persist them under ANY
64
+ * setting. The help text says so in exactly one place, built from
65
+ * {@link coldTierRestoreFamilyList} in `packages/cli/src/commands/agent/index.ts`
66
+ * and asserted by `packages/cli/__test__/agent-cmd.test.ts`.
67
+ */
68
+ export declare const COLD_TIER_RESTORE_FAMILIES: ReadonlySet<string>;
69
+ /** Allowlisted families in a stable, human-facing order (help text, API payload). */
70
+ export declare function coldTierRestoreFamilyList(): string[];
71
+ /**
72
+ * Canonical identity for a cold-cache root, used as the JOIN KEY between the
73
+ * agent that wrote a trace and the dashboard that reads it back.
74
+ *
75
+ * The two live in DIFFERENT PROCESSES resolving the root from their own
76
+ * environments: the native tier reports `manager.root().display()` (the path as
77
+ * constructed, never realpath'd) while the dashboard builds its own from
78
+ * `MLX_COLD_CACHE_DIR` or the `~/.mlx-node` default. On macOS `/tmp` is a
79
+ * symlink to `/private/tmp`, so two identical-looking spellings canonicalize
80
+ * differently — a raw string compare is a silent-zero trap, not a loud failure.
81
+ * Both sides MUST route through this one function.
82
+ *
83
+ * Resolution order: expand `~`, make absolute, then `realpath` the DEEPEST
84
+ * EXISTING ANCESTOR and re-attach every missing segment lexically. A tier that
85
+ * has never been opened has no root directory — that is the normal state, not
86
+ * an edge case — and when `MLX_COLD_CACHE_DIR` itself has never been created
87
+ * the parent is missing too. Stopping after one level returned a wholly
88
+ * UNcanonicalized path, so under a symlinked ancestor (`/tmp` → `/private/tmp`
89
+ * on macOS) the reader's key diverged from the writer's and rows from the SAME
90
+ * cache were reported as "a different cache directory". Only a path with no
91
+ * resolvable ancestor at all falls back to the lexical absolute form; this is
92
+ * an identity key, not a security boundary, so it fails OPEN to a stable string
93
+ * rather than to `undefined`.
94
+ *
95
+ * An empty input stays empty: the native struct reports `root: ''` while the
96
+ * tier is disabled, and that must never be recorded as a real cache identity.
97
+ */
98
+ export declare function canonicalCacheRoot(root: string): string;
99
+ //# sourceMappingURL=cold-tier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cold-tier.d.ts","sourceRoot":"","sources":["../src/cold-tier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,CAKzD,CAAC;AAEH,qFAAqF;AACrF,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAEpD;AA2BD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAmBvD"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Cold-tier facts shared by the agent (writer), the CLI (help text) and the
3
+ * dashboard (reader), kept in a NATIVE-FREE leaf module.
4
+ *
5
+ * `provider/model-host.ts` value-imports `@mlx-node/lm`, which loads the native
6
+ * addon; the dashboard is a separate viewer process that must never link it
7
+ * ("no Metal init, instant start" — docs/dashboard.md), and `mlx agent --help`
8
+ * must print without loading weights. This module therefore has NO runtime
9
+ * imports beyond `node:` builtins, and reaches those consumers through the
10
+ * existing native-free `@mlx-node/agent/catalog` subpath, which re-exports it.
11
+ * There is deliberately no `./cold-tier` entry in `package.json`: a second
12
+ * published subpath would be a second thing to keep native-free, and `catalog`
13
+ * already carries that guarantee.
14
+ *
15
+ * Everything here is a single source of truth. Do not copy the family list or
16
+ * re-implement the root canonicalization at a call site; import them.
17
+ */
18
+ import { realpathSync } from 'node:fs';
19
+ import { homedir } from 'node:os';
20
+ import { basename, dirname, join, resolve } from 'node:path';
21
+ /**
22
+ * Model families whose paged prefix blocks can be restored from the SSD cold
23
+ * tier soundly. The single source of truth on the TypeScript side, mirroring
24
+ * `COLD_RESTORE_FAMILIES` in `crates/mlx-core/src/cold_tier.rs`; the two gate
25
+ * the same decision from opposite ends, and
26
+ * `packages/agent/__test__/cold-tier-families.test.ts` asserts they never
27
+ * drift.
28
+ *
29
+ * A family belongs here only when EVERY piece of per-token state its forward
30
+ * pass carries between turns is reconstructible from the tier — either because
31
+ * it all lives inside the paged pool, or because the part that does not is
32
+ * persisted alongside as a cold-tier sidecar and the restore reconciles down to
33
+ * a boundary that sidecar actually backs:
34
+ *
35
+ * - `qwen3` (dense) sizes its pool over all layers, so the pool holds the
36
+ * complete KV for the prefix and needs no sidecar.
37
+ * - `gemma4` sizes its pool over the full-attention layers only, but persists
38
+ * its out-of-pool sliding-window `RotatingKVCache` state as a
39
+ * `ColdGroup::SlidingWindow` sidecar, and its `ColdSidecarPolicy` makes the
40
+ * native restore walk refuse any boundary a validated sidecar does not back.
41
+ * - `qwen3_5` (dense) sizes its pool over the full-attention layers only, but
42
+ * persists its out-of-pool GDN recurrent state (conv + recurrent) as a
43
+ * `ColdGroup::GdnState` sidecar, and its `ColdSidecarPolicy` reconciles the
44
+ * native restore down to the deepest block-aligned boundary a validated
45
+ * sidecar backs (a recurrent state is valid only at its exact prefix length).
46
+ * - `qwen3_5_moe` keeps the SAME GDN recurrent state outside the pool — same
47
+ * shapes, same dtype, same layer mapping — so it shares the dense family's
48
+ * sidecar codec and `ColdSidecarPolicy` verbatim. Its restart-parity gate has
49
+ * since been run on `Qwen3.6-35b-a3b-UD-Q2_K_XL-mlx`: the restore reconciled
50
+ * onto ladder rung 304 of `[16, 64, 304, 1248]` with `hits=42` and
51
+ * `corruptions=0`, and the text matched a no-persist baseline.
52
+ * - `lfm2` / `lfm2_moe` keep short-conv state outside the pool with no
53
+ * serialization path for it at all, AND drive the uniform native adapter
54
+ * API whose restore branch is already wired to the tier — asking for
55
+ * persistence on their behalf would restore an incomplete prefix silently.
56
+ *
57
+ * Widening this set is a correctness decision, never a perf one, and it is
58
+ * authorized by exactly one thing: the family's native restart-parity gate
59
+ * passing on real weights with `hits > 0` and `corruptions == 0`.
60
+ *
61
+ * `mlx agent --no-persist-cache` applies to EVERY family in this set, not to
62
+ * one of them: it is a SINGLE process-wide boolean handed to every load whose
63
+ * family is on the allowlist (`MlxModelHost.runWithResident` →
64
+ * `resolveModelPathFn(entry, { persistPagedCache })`). Families off the
65
+ * allowlist are handed no policy at all — not because the flag spares them, but
66
+ * because `cold_tier::resolve_persist_cold` refuses to persist them under ANY
67
+ * setting. The help text says so in exactly one place, built from
68
+ * {@link coldTierRestoreFamilyList} in `packages/cli/src/commands/agent/index.ts`
69
+ * and asserted by `packages/cli/__test__/agent-cmd.test.ts`.
70
+ */
71
+ export const COLD_TIER_RESTORE_FAMILIES = new Set([
72
+ 'gemma4',
73
+ 'qwen3',
74
+ 'qwen3_5',
75
+ 'qwen3_5_moe',
76
+ ]);
77
+ /** Allowlisted families in a stable, human-facing order (help text, API payload). */
78
+ export function coldTierRestoreFamilyList() {
79
+ return [...COLD_TIER_RESTORE_FAMILIES].sort();
80
+ }
81
+ /**
82
+ * Expand a leading `~` / `~/…` against the current home directory. A bare `~`
83
+ * prefix that is part of a longer name (`~cache`) is left alone — only the
84
+ * shell's own two spellings are expanded.
85
+ */
86
+ /**
87
+ * `realpath(3)` rather than Node's JS walk.
88
+ *
89
+ * On a case-insensitive volume — APFS's default, i.e. essentially every Mac —
90
+ * `MLX_COLD_CACHE_DIR=~/CacheDir` and `~/cachedir` name the SAME directory, but
91
+ * `fs.realpathSync` hands back whichever spelling the caller typed. Two join
92
+ * keys for one cache is the F1 symptom inverted: a populated disk scan sitting
93
+ * beside a flat 0/0 trend. `realpathSync.native` folds each existing component
94
+ * to its on-disk spelling, so both env spellings produce one key.
95
+ */
96
+ function realpathNative(path) {
97
+ return realpathSync.native(path);
98
+ }
99
+ function expandTilde(path) {
100
+ if (path === '~')
101
+ return homedir();
102
+ if (path.startsWith('~/'))
103
+ return join(homedir(), path.slice(2));
104
+ return path;
105
+ }
106
+ /**
107
+ * Canonical identity for a cold-cache root, used as the JOIN KEY between the
108
+ * agent that wrote a trace and the dashboard that reads it back.
109
+ *
110
+ * The two live in DIFFERENT PROCESSES resolving the root from their own
111
+ * environments: the native tier reports `manager.root().display()` (the path as
112
+ * constructed, never realpath'd) while the dashboard builds its own from
113
+ * `MLX_COLD_CACHE_DIR` or the `~/.mlx-node` default. On macOS `/tmp` is a
114
+ * symlink to `/private/tmp`, so two identical-looking spellings canonicalize
115
+ * differently — a raw string compare is a silent-zero trap, not a loud failure.
116
+ * Both sides MUST route through this one function.
117
+ *
118
+ * Resolution order: expand `~`, make absolute, then `realpath` the DEEPEST
119
+ * EXISTING ANCESTOR and re-attach every missing segment lexically. A tier that
120
+ * has never been opened has no root directory — that is the normal state, not
121
+ * an edge case — and when `MLX_COLD_CACHE_DIR` itself has never been created
122
+ * the parent is missing too. Stopping after one level returned a wholly
123
+ * UNcanonicalized path, so under a symlinked ancestor (`/tmp` → `/private/tmp`
124
+ * on macOS) the reader's key diverged from the writer's and rows from the SAME
125
+ * cache were reported as "a different cache directory". Only a path with no
126
+ * resolvable ancestor at all falls back to the lexical absolute form; this is
127
+ * an identity key, not a security boundary, so it fails OPEN to a stable string
128
+ * rather than to `undefined`.
129
+ *
130
+ * An empty input stays empty: the native struct reports `root: ''` while the
131
+ * tier is disabled, and that must never be recorded as a real cache identity.
132
+ */
133
+ export function canonicalCacheRoot(root) {
134
+ const expanded = expandTilde(root.trim());
135
+ if (expanded.length === 0)
136
+ return '';
137
+ const absolute = resolve(expanded);
138
+ const missing = [];
139
+ let cursor = absolute;
140
+ for (;;) {
141
+ try {
142
+ const resolved = realpathNative(cursor);
143
+ return missing.length === 0 ? resolved : join(resolved, ...missing);
144
+ }
145
+ catch {
146
+ const parent = dirname(cursor);
147
+ // `dirname` is a fixpoint at the filesystem root, which is the loop's
148
+ // termination condition: nothing above it is left to canonicalize.
149
+ if (parent === cursor)
150
+ return absolute;
151
+ missing.unshift(basename(cursor));
152
+ cursor = parent;
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Attach the local image path inserted by Pi's interactive clipboard handler.
3
+ *
4
+ * Pi 0.81.1 writes a pasted clipboard image to a temporary file and inserts
5
+ * only that absolute path into the editor. AgentSession's `input` event is the
6
+ * last seam where the path can be upgraded to `ImageContent` before the user
7
+ * message is committed. Keep this deliberately narrow:
8
+ *
9
+ * - TUI + interactive input only;
10
+ * - the entire input must be one absolute image path;
11
+ * - only `\ ` shell escapes (the macOS drag/paste shape) are decoded;
12
+ * - extension and magic bytes must both identify a supported image.
13
+ *
14
+ * Every failed check returns `continue`, preserving the original text exactly.
15
+ * Capability is intentionally not checked here: discovery advertises every
16
+ * local model as text-only until its first resident load. The stream adapter's
17
+ * authoritative `session.supportsImages()` decides whether native bytes or the
18
+ * existing text-model placeholder reaches inference.
19
+ */
20
+ import type { ExtensionContext, InlineExtension, InputEvent, InputEventResult } from '@earendil-works/pi-coding-agent';
21
+ /** @internal Exported for focused tests of Pi's input-transform contract. */
22
+ export declare function attachStandaloneLocalImage(event: InputEvent, ctx: ExtensionContext): Promise<InputEventResult>;
23
+ export declare function createLocalImageInputExtension(): InlineExtension;
24
+ //# sourceMappingURL=local-image-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-image-input.d.ts","sourceRoot":"","sources":["../../src/extensions/local-image-input.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,KAAK,EAEV,gBAAgB,EAChB,eAAe,EACf,UAAU,EACV,gBAAgB,EACjB,MAAM,iCAAiC,CAAC;AA0CzC,6EAA6E;AAC7E,wBAAsB,0BAA0B,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkDpH;AAED,wBAAgB,8BAA8B,IAAI,eAAe,CAOhE"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Attach the local image path inserted by Pi's interactive clipboard handler.
3
+ *
4
+ * Pi 0.81.1 writes a pasted clipboard image to a temporary file and inserts
5
+ * only that absolute path into the editor. AgentSession's `input` event is the
6
+ * last seam where the path can be upgraded to `ImageContent` before the user
7
+ * message is committed. Keep this deliberately narrow:
8
+ *
9
+ * - TUI + interactive input only;
10
+ * - the entire input must be one absolute image path;
11
+ * - only `\ ` shell escapes (the macOS drag/paste shape) are decoded;
12
+ * - extension and magic bytes must both identify a supported image.
13
+ *
14
+ * Every failed check returns `continue`, preserving the original text exactly.
15
+ * Capability is intentionally not checked here: discovery advertises every
16
+ * local model as text-only until its first resident load. The stream adapter's
17
+ * authoritative `session.supportsImages()` decides whether native bytes or the
18
+ * existing text-model placeholder reaches inference.
19
+ */
20
+ import { open } from 'node:fs/promises';
21
+ import { extname, isAbsolute } from 'node:path';
22
+ const MAX_LOCAL_IMAGE_BYTES = 20 * 1024 * 1024;
23
+ const IMAGE_EXTENSIONS = new Set(['.gif', '.jpeg', '.jpg', '.png', '.webp']);
24
+ const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
25
+ function startsWith(bytes, signature, offset = 0) {
26
+ return (bytes.byteLength >= offset + signature.length && signature.every((byte, index) => bytes[offset + index] === byte));
27
+ }
28
+ function startsWithAscii(bytes, text, offset = 0) {
29
+ return startsWith(bytes, Array.from(text, (character) => character.charCodeAt(0)), offset);
30
+ }
31
+ function detectImageMimeType(bytes) {
32
+ if (startsWith(bytes, [0xff, 0xd8, 0xff]) && bytes[3] !== 0xf7)
33
+ return 'image/jpeg';
34
+ if (startsWith(bytes, PNG_SIGNATURE) && startsWithAscii(bytes, 'IHDR', 12))
35
+ return 'image/png';
36
+ if (startsWithAscii(bytes, 'GIF87a') || startsWithAscii(bytes, 'GIF89a'))
37
+ return 'image/gif';
38
+ if (startsWithAscii(bytes, 'RIFF') && startsWithAscii(bytes, 'WEBP', 8))
39
+ return 'image/webp';
40
+ return undefined;
41
+ }
42
+ /**
43
+ * Resolve only the standalone path shape produced by Pi/macOS paste and drag.
44
+ * This is not a shell parser: no quoting, expansion, commands, or general
45
+ * backslash processing is performed.
46
+ */
47
+ function standaloneImagePath(text) {
48
+ const trimmed = text.trim();
49
+ if (trimmed.length === 0 || trimmed.includes('\n') || trimmed.includes('\r'))
50
+ return undefined;
51
+ const path = trimmed.replaceAll('\\ ', ' ');
52
+ if (!isAbsolute(path) || !IMAGE_EXTENSIONS.has(extname(path).toLowerCase()))
53
+ return undefined;
54
+ return path;
55
+ }
56
+ /** @internal Exported for focused tests of Pi's input-transform contract. */
57
+ export async function attachStandaloneLocalImage(event, ctx) {
58
+ if (ctx.mode !== 'tui' || event.source !== 'interactive' || event.images?.length) {
59
+ return { action: 'continue' };
60
+ }
61
+ const path = standaloneImagePath(event.text);
62
+ if (path === undefined)
63
+ return { action: 'continue' };
64
+ try {
65
+ // Open once so validation and reading stay bound to the same inode if
66
+ // the pathname is replaced. FileHandle.readFile() is intentionally not
67
+ // used: it may allocate from a file that grew after stat(). The exact,
68
+ // positional loop below caps allocation at the validated size.
69
+ const file = await open(path, 'r');
70
+ try {
71
+ const metadata = await file.stat();
72
+ if (!metadata.isFile() || metadata.size === 0 || metadata.size > MAX_LOCAL_IMAGE_BYTES) {
73
+ return { action: 'continue' };
74
+ }
75
+ const bytes = Buffer.allocUnsafe(metadata.size);
76
+ let offset = 0;
77
+ while (offset < bytes.byteLength) {
78
+ const { bytesRead } = await file.read(bytes, offset, bytes.byteLength - offset, offset);
79
+ if (bytesRead === 0)
80
+ return { action: 'continue' };
81
+ offset += bytesRead;
82
+ }
83
+ // Reject same-inode growth after fstat without ever allocating or
84
+ // reading more than one byte beyond the validated size.
85
+ const overflowProbe = Buffer.allocUnsafe(1);
86
+ if ((await file.read(overflowProbe, 0, 1, metadata.size)).bytesRead !== 0) {
87
+ return { action: 'continue' };
88
+ }
89
+ const mimeType = detectImageMimeType(bytes);
90
+ if (mimeType === undefined)
91
+ return { action: 'continue' };
92
+ return {
93
+ action: 'transform',
94
+ text: event.text,
95
+ images: [{ type: 'image', mimeType, data: bytes.toString('base64') }],
96
+ };
97
+ }
98
+ finally {
99
+ await file.close();
100
+ }
101
+ }
102
+ catch {
103
+ // Missing, unreadable, or racing files remain ordinary user text.
104
+ return { action: 'continue' };
105
+ }
106
+ }
107
+ export function createLocalImageInputExtension() {
108
+ return {
109
+ name: 'mlx-local-image-input',
110
+ factory: (pi) => {
111
+ pi.on('input', attachStandaloneLocalImage);
112
+ },
113
+ };
114
+ }
@@ -15,6 +15,16 @@
15
15
  * examples/extensions/subagent (MIT).
16
16
  */
17
17
  import type { ExtensionContext, InlineExtension } from '@earendil-works/pi-coding-agent';
18
+ /**
19
+ * Tool loops can overlap; the shared `MlxModelHost` serializes model calls.
20
+ *
21
+ * Exported because it is one half of a cross-language invariant. Every live
22
+ * loop is a distinct native GDN cache owner, so this fleet demands
23
+ * `MAX_CONCURRENCY + 1` checkpoint slots (root session plus one per loop), and
24
+ * the native store supplies `gdnPrefixCheckpointLimit()` of them.
25
+ * `__test__/gdn-checkpoint-capacity.test.ts` holds the two together.
26
+ */
27
+ export declare const MAX_CONCURRENCY = 4;
18
28
  type AgentScope = 'user' | 'project' | 'both';
19
29
  type AgentSource = 'builtin' | 'user' | 'project' | 'unknown';
20
30
  export type SubagentMode = 'single' | 'parallel' | 'chain';
@@ -36,7 +46,15 @@ export interface InProcessSubagentSession {
36
46
  export interface SubagentSessionCreateOptions {
37
47
  cwd: string;
38
48
  model: NonNullable<ExtensionContext['model']>;
39
- modelRegistry: ExtensionContext['modelRegistry'];
49
+ /**
50
+ * Parent's registered in-process mlx provider config. Registering it on the
51
+ * subagent's fresh runtime reuses the parent's `streamSimple` closure, which
52
+ * is bound to the single shared `MlxModelHost` — so subagent inference stays
53
+ * serialized on the one GPU-resident model. `ProviderConfigInput` is not
54
+ * exported at the package root, so type it structurally from the facade
55
+ * method's return type (survives a future pi rename of the type).
56
+ */
57
+ mlxProviderConfig: NonNullable<ReturnType<ExtensionContext['modelRegistry']['getRegisteredProviderConfig']>>;
40
58
  tools?: string[];
41
59
  systemPrompt: string;
42
60
  }
@@ -77,6 +95,7 @@ export declare function normalizeSubagentMode(input: SubagentRequestShape): {
77
95
  mode: SubagentMode;
78
96
  modeCount: number;
79
97
  };
98
+ export declare function createProductionSession(options: SubagentSessionCreateOptions): Promise<InProcessSubagentSession>;
80
99
  export declare function createSubagentExtension(options?: SubagentExtensionOptions): InlineExtension;
81
100
  export {};
82
101
  //# sourceMappingURL=subagent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../src/extensions/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EAAgB,gBAAgB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAUvG,KAAK,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAC9C,KAAK,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiCD,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC9F;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,0BAA0B,GAClC,0BAA0B,CAoB5B;AA6GD,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,GAChB;IACD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CASA;AAqCD,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,GAAG;IAClE,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAQA;AAwND,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,eAAe,CAsJ/F"}
1
+ {"version":3,"file":"subagent.d.ts","sourceRoot":"","sources":["../../src/extensions/subagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,OAAO,KAAK,EAAgB,gBAAgB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAMvG;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AAGjC,KAAK,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAC9C,KAAK,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiCD,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9C;;;;;;;OAOG;IACH,iBAAiB,EAAE,WAAW,CAAC,UAAU,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC;IAC7G,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC9F;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAMD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,0BAA0B,GAClC,0BAA0B,CAoB5B;AA6GD,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,GAChB;IACD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,CASA;AAqCD,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,GAAG;IAClE,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAQA;AAeD,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,wBAAwB,CAAC,CAkDnC;AA0LD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,wBAA6B,GAAG,eAAe,CAsJ/F"}
@@ -21,8 +21,16 @@ import { StringEnum } from '@earendil-works/pi-ai';
21
21
  import { Type } from 'typebox';
22
22
  import { sanitizeApprovalDetail } from './approval-detail.js';
23
23
  const MAX_PARALLEL_TASKS = 8;
24
- /** Tool loops can overlap; the shared `MlxModelHost` serializes model calls. */
25
- const MAX_CONCURRENCY = 4;
24
+ /**
25
+ * Tool loops can overlap; the shared `MlxModelHost` serializes model calls.
26
+ *
27
+ * Exported because it is one half of a cross-language invariant. Every live
28
+ * loop is a distinct native GDN cache owner, so this fleet demands
29
+ * `MAX_CONCURRENCY + 1` checkpoint slots (root session plus one per loop), and
30
+ * the native store supplies `gdnPrefixCheckpointLimit()` of them.
31
+ * `__test__/gdn-checkpoint-capacity.test.ts` holds the two together.
32
+ */
33
+ export const MAX_CONCURRENCY = 4;
26
34
  const PER_TASK_OUTPUT_CAP = 50 * 1024;
27
35
  const COMPACTION_RESERVE_WINDOW_FRACTION = 0.25;
28
36
  const COMPACTION_KEEP_RECENT_WINDOW_FRACTION = 0.5;
@@ -238,10 +246,10 @@ async function mapWithConcurrencyLimit(items, fn) {
238
246
  await Promise.all(workers);
239
247
  return results;
240
248
  }
241
- async function createProductionSession(options) {
249
+ export async function createProductionSession(options) {
242
250
  // `runAgent()` seeds pi's config environment before the extension can execute,
243
251
  // so defer the runtime import until now instead of importing pi at module load.
244
- const { createAgentSession, DefaultResourceLoader, getAgentDir, SessionManager, SettingsManager } = await import('@earendil-works/pi-coding-agent');
252
+ const { createAgentSession, DefaultResourceLoader, getAgentDir, ModelRuntime, SessionManager, SettingsManager } = await import('@earendil-works/pi-coding-agent');
245
253
  const agentDir = getAgentDir();
246
254
  const settingsManager = SettingsManager.create(options.cwd, agentDir);
247
255
  settingsManager.applyOverrides({
@@ -259,11 +267,26 @@ async function createProductionSession(options) {
259
267
  appendSystemPromptOverride: (base) => (options.systemPrompt.trim() ? [...base, options.systemPrompt] : base),
260
268
  });
261
269
  await resourceLoader.reload();
270
+ // 0.81.1: `createAgentSession` takes a `ModelRuntime`, not a `ModelRegistry`.
271
+ // Build a fresh runtime that carries the parent's in-process mlx provider. The
272
+ // config's `streamSimple` closure is bound to the single shared `MlxModelHost`,
273
+ // so all subagent inference stays serialized on the one GPU-resident model.
274
+ // `create` does no network (`allowModelNetwork:false`, also the default — set
275
+ // explicitly to keep the offline invariant local to this call, independent of
276
+ // the process-wide PI_OFFLINE seed in `runAgent`), and reuses the parent's
277
+ // `authPath`/`modelsPath`; `registerProvider` marks mlx configured (apiKey
278
+ // present) so streaming works immediately, dispatched by provider id.
279
+ const modelRuntime = await ModelRuntime.create({
280
+ authPath: path.join(agentDir, 'auth.json'),
281
+ modelsPath: path.join(agentDir, 'models.json'),
282
+ allowModelNetwork: false,
283
+ });
284
+ modelRuntime.registerProvider('mlx', options.mlxProviderConfig);
262
285
  const { session } = await createAgentSession({
263
286
  cwd: options.cwd,
264
287
  agentDir,
265
288
  model: options.model,
266
- modelRegistry: options.modelRegistry,
289
+ modelRuntime,
267
290
  tools: options.tools,
268
291
  resourceLoader,
269
292
  sessionManager: SessionManager.inMemory(options.cwd),
@@ -312,6 +335,23 @@ async function runSingleAgent(options, defaultCwd, agents, agentName, task, cwd,
312
335
  step,
313
336
  };
314
337
  }
338
+ // The subagent runs on a fresh runtime (no mlx provider extension), so it must
339
+ // inherit the parent's exact registered mlx config to reuse the shared host.
340
+ // Fail closed if the parent has no 'mlx' provider — never fall back to an
341
+ // unconfigured/cloud provider.
342
+ const mlxProviderConfig = context.modelRegistry.getRegisteredProviderConfig('mlx');
343
+ if (!mlxProviderConfig) {
344
+ return {
345
+ agent: agent.name,
346
+ agentSource: agent.source,
347
+ task,
348
+ exitCode: 1,
349
+ messages: [],
350
+ stderr: `Agent ${agent.name} could not inherit the parent mlx provider (no 'mlx' provider registered).`,
351
+ usage: emptyUsage(),
352
+ step,
353
+ };
354
+ }
315
355
  const result = {
316
356
  agent: agent.name,
317
357
  agentSource: agent.source,
@@ -342,7 +382,7 @@ async function runSingleAgent(options, defaultCwd, agents, agentName, task, cwd,
342
382
  session = await (options.createSession ?? createProductionSession)({
343
383
  cwd: cwd ?? defaultCwd,
344
384
  model,
345
- modelRegistry: context.modelRegistry,
385
+ mlxProviderConfig,
346
386
  tools: agent.tools,
347
387
  systemPrompt: agent.systemPrompt,
348
388
  });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `$HOME/.mlx-node` layout helpers owned by the agent.
3
+ *
4
+ * The agent must not import `@mlx-node/cli` (wrong dependency direction), so
5
+ * the small home-directory layout it needs lives here. Mirrors
6
+ * `resolveMlxNodeHome()` in `@mlx-node/server/host/paths`
7
+ * (`packages/server/src/host/paths.ts`).
8
+ */
9
+ /** Absolute path to `$HOME/.mlx-node`. */
10
+ export declare function mlxNodeHome(): string;
11
+ /** Directory holding per-process `MetricsTrace` JSONL files. */
12
+ export declare function metricsTraceDir(): string;
13
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,0CAA0C;AAC1C,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,gEAAgE;AAChE,wBAAgB,eAAe,IAAI,MAAM,CAExC"}
package/dist/paths.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `$HOME/.mlx-node` layout helpers owned by the agent.
3
+ *
4
+ * The agent must not import `@mlx-node/cli` (wrong dependency direction), so
5
+ * the small home-directory layout it needs lives here. Mirrors
6
+ * `resolveMlxNodeHome()` in `@mlx-node/server/host/paths`
7
+ * (`packages/server/src/host/paths.ts`).
8
+ */
9
+ import { homedir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ /** Absolute path to `$HOME/.mlx-node`. */
12
+ export function mlxNodeHome() {
13
+ return join(homedir(), '.mlx-node');
14
+ }
15
+ /** Directory holding per-process `MetricsTrace` JSONL files. */
16
+ export function metricsTraceDir() {
17
+ return join(mlxNodeHome(), 'metrics', 'traces');
18
+ }
@@ -30,5 +30,5 @@ export interface ResolvedReasoningMode {
30
30
  * option was present.
31
31
  */
32
32
  export declare function resolveReasoningMode(reasoning: ThinkingLevel | undefined): ResolvedReasoningMode;
33
- export declare function buildChatConfig(modelType: ModelType, options: SimpleStreamOptions | undefined, tools: ToolDefinition[] | undefined, rootCacheOwnerId?: string, resolvedReasoning?: ResolvedReasoningMode): ChatConfig;
33
+ export declare function buildChatConfig(modelType: ModelType, options: SimpleStreamOptions | undefined, tools: ToolDefinition[] | undefined, rootCacheOwnerId?: string, resolvedReasoning?: ResolvedReasoningMode, modelMaxTokens?: unknown): ChatConfig;
34
34
  //# sourceMappingURL=chat-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-config.d.ts","sourceRoot":"","sources":["../../src/provider/chat-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA0BrE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAE9E;AAgBD,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpD,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,qBAAqB,CAMhG;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,mBAAmB,GAAG,SAAS,EACxC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,EACnC,gBAAgB,CAAC,EAAE,MAAM,EACzB,iBAAiB,wBAA2C,GAC3D,UAAU,CA6BZ"}
1
+ {"version":3,"file":"chat-config.d.ts","sourceRoot":"","sources":["../../src/provider/chat-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA0BrE;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAE9E;AAgBD,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpD,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAMD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,qBAAqB,CAMhG;AAED,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,mBAAmB,GAAG,SAAS,EACxC,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,EACnC,gBAAgB,CAAC,EAAE,MAAM,EACzB,iBAAiB,wBAA2C,EAC5D,cAAc,CAAC,EAAE,OAAO,GACvB,UAAU,CAsCZ"}