@mlx-node/agent 0.0.8 → 0.0.9
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.
- package/dist/catalog.d.ts +15 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +15 -0
- package/dist/cold-tier.d.ts +99 -0
- package/dist/cold-tier.d.ts.map +1 -0
- package/dist/cold-tier.js +155 -0
- package/dist/extensions/local-image-input.d.ts +24 -0
- package/dist/extensions/local-image-input.d.ts.map +1 -0
- package/dist/extensions/local-image-input.js +114 -0
- package/dist/extensions/subagent.d.ts +20 -1
- package/dist/extensions/subagent.d.ts.map +1 -1
- package/dist/extensions/subagent.js +46 -6
- package/dist/paths.d.ts +13 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +18 -0
- package/dist/provider/events.d.ts +9 -0
- package/dist/provider/events.d.ts.map +1 -1
- package/dist/provider/events.js +15 -0
- package/dist/provider/index.d.ts +12 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +153 -7
- package/dist/provider/metrics-trace.d.ts +274 -0
- package/dist/provider/metrics-trace.d.ts.map +1 -0
- package/dist/provider/metrics-trace.js +174 -0
- package/dist/provider/mlx-identity.d.ts +16 -0
- package/dist/provider/mlx-identity.d.ts.map +1 -0
- package/dist/provider/mlx-identity.js +15 -0
- package/dist/provider/model-host.d.ts +45 -3
- package/dist/provider/model-host.d.ts.map +1 -1
- package/dist/provider/model-host.js +34 -2
- package/dist/provider/model-registry-filter.d.ts +74 -18
- package/dist/provider/model-registry-filter.d.ts.map +1 -1
- package/dist/provider/model-registry-filter.js +229 -38
- package/dist/provider/models.d.ts +2 -3
- package/dist/provider/models.d.ts.map +1 -1
- package/dist/provider/models.js +46 -20
- package/dist/provider/stream-adapter.d.ts +37 -4
- package/dist/provider/stream-adapter.d.ts.map +1 -1
- package/dist/provider/stream-adapter.js +58 -3
- package/dist/provider/warm-reuse.d.ts +11 -8
- package/dist/provider/warm-reuse.d.ts.map +1 -1
- package/dist/provider/warm-reuse.js +14 -7
- package/dist/run-agent.d.ts +12 -3
- package/dist/run-agent.d.ts.map +1 -1
- package/dist/run-agent.js +31 -5
- package/package.json +10 -5
|
@@ -18,18 +18,19 @@
|
|
|
18
18
|
* the reused prefix and skip the corresponding re-prefill.
|
|
19
19
|
*
|
|
20
20
|
* Fields accessed: `inFlight`, `history`, `lastImagesKey`, `lastAudioKey`, `turnCount`,
|
|
21
|
-
* `unresolvedOkToolCallCount`, `needsFullReplay
|
|
22
|
-
* `ChatSession` (compile-time
|
|
23
|
-
* properties. The cast through
|
|
24
|
-
* gives this helper a typed view
|
|
25
|
-
* class's `private` declarations.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* `unresolvedOkToolCallCount`, `needsFullReplay`, `defaultConfig`, `activeTools`.
|
|
22
|
+
* These are TypeScript `private` fields on `ChatSession` (compile-time
|
|
23
|
+
* only) — at runtime they are ordinary properties. The cast through
|
|
24
|
+
* {@link ChatSessionWarmReuseInternals} gives this helper a typed view
|
|
25
|
+
* of the instance without relaxing the class's `private` declarations.
|
|
26
|
+
* The field names MUST stay in sync with
|
|
27
|
+
* `packages/lm/src/chat-session.ts`; a mismatch would silently skip
|
|
28
|
+
* the intended state wipe — the drift test in
|
|
28
29
|
* `packages/agent/__test__/warm-reuse.test.ts` checks every name in
|
|
29
30
|
* {@link WARM_REUSE_TOUCHED_FIELDS} against a real `ChatSession`
|
|
30
31
|
* instance.
|
|
31
32
|
*/
|
|
32
|
-
import type { ChatSession, SessionCapableModel } from '@mlx-node/lm';
|
|
33
|
+
import type { ChatConfig, ChatSession, SessionCapableModel } from '@mlx-node/lm';
|
|
33
34
|
/**
|
|
34
35
|
* Private structural view of the `ChatSession` JS-side state that the
|
|
35
36
|
* warm-reuse helper needs to wipe. Mirrors the internal state
|
|
@@ -45,6 +46,8 @@ interface ChatSessionWarmReuseInternals {
|
|
|
45
46
|
turnCount: number;
|
|
46
47
|
unresolvedOkToolCallCount: number | null;
|
|
47
48
|
needsFullReplay: boolean;
|
|
49
|
+
defaultConfig?: ChatConfig;
|
|
50
|
+
activeTools: ChatConfig['tools'];
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
50
53
|
* Runtime list of the `ChatSession` private field names this module
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"warm-reuse.d.ts","sourceRoot":"","sources":["../../src/provider/warm-reuse.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"warm-reuse.d.ts","sourceRoot":"","sources":["../../src/provider/warm-reuse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEjF;;;;;;GAMG;AACH,UAAU,6BAA6B;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,EAAE,CAAC;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,WAAW,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;CAClC;AAoBD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAgD,aAAa,CACjG,MAAM,6BAA6B,CACpC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sCAAsC,CAAC,CAAC,SAAS,mBAAmB,EACxF,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
* the reused prefix and skip the corresponding re-prefill.
|
|
19
19
|
*
|
|
20
20
|
* Fields accessed: `inFlight`, `history`, `lastImagesKey`, `lastAudioKey`, `turnCount`,
|
|
21
|
-
* `unresolvedOkToolCallCount`, `needsFullReplay
|
|
22
|
-
* `ChatSession` (compile-time
|
|
23
|
-
* properties. The cast through
|
|
24
|
-
* gives this helper a typed view
|
|
25
|
-
* class's `private` declarations.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* `unresolvedOkToolCallCount`, `needsFullReplay`, `defaultConfig`, `activeTools`.
|
|
22
|
+
* These are TypeScript `private` fields on `ChatSession` (compile-time
|
|
23
|
+
* only) — at runtime they are ordinary properties. The cast through
|
|
24
|
+
* {@link ChatSessionWarmReuseInternals} gives this helper a typed view
|
|
25
|
+
* of the instance without relaxing the class's `private` declarations.
|
|
26
|
+
* The field names MUST stay in sync with
|
|
27
|
+
* `packages/lm/src/chat-session.ts`; a mismatch would silently skip
|
|
28
|
+
* the intended state wipe — the drift test in
|
|
28
29
|
* `packages/agent/__test__/warm-reuse.test.ts` checks every name in
|
|
29
30
|
* {@link WARM_REUSE_TOUCHED_FIELDS} against a real `ChatSession`
|
|
30
31
|
* instance.
|
|
@@ -43,6 +44,8 @@ const WARM_REUSE_TOUCHED_FIELD_SET = {
|
|
|
43
44
|
turnCount: true,
|
|
44
45
|
unresolvedOkToolCallCount: true,
|
|
45
46
|
needsFullReplay: true,
|
|
47
|
+
defaultConfig: true,
|
|
48
|
+
activeTools: true,
|
|
46
49
|
};
|
|
47
50
|
/**
|
|
48
51
|
* Runtime list of the `ChatSession` private field names this module
|
|
@@ -85,4 +88,8 @@ export async function resetPreservingNativeCacheForWarmReuse(session) {
|
|
|
85
88
|
internals.turnCount = 0;
|
|
86
89
|
internals.unresolvedOkToolCallCount = null;
|
|
87
90
|
internals.needsFullReplay = false;
|
|
91
|
+
// Tools are conversation state. A provider replay can switch to an
|
|
92
|
+
// unrelated history, so restore constructor defaults exactly like
|
|
93
|
+
// ChatSession.reset() instead of leaking the prior committed overlay.
|
|
94
|
+
internals.activeTools = internals.defaultConfig?.tools;
|
|
88
95
|
}
|
package/dist/run-agent.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* critical on pi's hard `process.exit()` paths.
|
|
18
18
|
*/
|
|
19
19
|
import type { InlineExtension } from '@earendil-works/pi-coding-agent';
|
|
20
|
-
import { type
|
|
20
|
+
import { type FilterableModelRuntimeConstructor } from './provider/model-registry-filter.js';
|
|
21
21
|
import type { MlxModelInfo } from './provider/models.js';
|
|
22
22
|
/** Shape of pi's `main(argv, { extensionFactories })` — also the test seam. */
|
|
23
23
|
export type RunAgentMain = (args: string[], opts: {
|
|
@@ -25,11 +25,11 @@ export type RunAgentMain = (args: string[], opts: {
|
|
|
25
25
|
}) => Promise<void>;
|
|
26
26
|
export interface RunAgentPi {
|
|
27
27
|
main: RunAgentMain;
|
|
28
|
-
|
|
28
|
+
ModelRuntime: FilterableModelRuntimeConstructor;
|
|
29
29
|
}
|
|
30
30
|
/** @internal Narrow lifecycle seam for the agent's paged config overlays. */
|
|
31
31
|
export interface AgentPagedConfigOverrides {
|
|
32
|
-
resolve(modelPath: string, modelType?: string): Promise<string>;
|
|
32
|
+
resolve(modelPath: string, modelType?: string, persistPagedCache?: boolean): Promise<string>;
|
|
33
33
|
cleanup(): Promise<void>;
|
|
34
34
|
}
|
|
35
35
|
export interface RunAgentOptions {
|
|
@@ -41,6 +41,15 @@ export interface RunAgentOptions {
|
|
|
41
41
|
argv: string[];
|
|
42
42
|
/** Native inference-log path to surface after Pi takes over the TUI. */
|
|
43
43
|
traceLogFile?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Enable the SSD cold tier by default (the agent's default; the CLI sets it
|
|
46
|
+
* false for `--no-persist-cache`). Forwarded to {@link MlxModelHost}, which
|
|
47
|
+
* applies this ONE value to every load whose family is in
|
|
48
|
+
* `COLD_TIER_RESTORE_FAMILIES` — not to qwen3 alone. Families off that list
|
|
49
|
+
* are handed no policy because they can never persist, not because this flag
|
|
50
|
+
* spares them. `undefined` keeps the host's on-by-default behavior.
|
|
51
|
+
*/
|
|
52
|
+
persistPagedCache?: boolean;
|
|
44
53
|
/** @internal Test seam; when set, the pi dynamic import is skipped entirely. */
|
|
45
54
|
piImpl?: RunAgentPi;
|
|
46
55
|
/** @internal Test seam for paged model-path resolution and cleanup. */
|
package/dist/run-agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-agent.d.ts","sourceRoot":"","sources":["../src/run-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"run-agent.d.ts","sourceRoot":"","sources":["../src/run-agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAWvE,OAAO,EACL,KAAK,iCAAiC,EAEvC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,+EAA+E;AAC/E,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE;IAAE,kBAAkB,EAAE,eAAe,EAAE,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9G,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,YAAY,EAAE,iCAAiC,CAAC;CACjD;AAED,6EAA6E;AAC7E,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7F,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,yDAAyD;IACzD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,gFAAgF;IAChF,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,yBAAyB,CAAC;CAClD;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAEpF;AAED;;;;;;;;;GASG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA2EnE"}
|
package/dist/run-agent.js
CHANGED
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { homedir } from 'node:os';
|
|
20
20
|
import { join } from 'node:path';
|
|
21
|
+
import { coldCacheDrain } from '@mlx-node/core';
|
|
21
22
|
import { PagedConfigOverrideManager } from '@mlx-node/lm';
|
|
23
|
+
import { createLocalImageInputExtension } from './extensions/local-image-input.js';
|
|
22
24
|
import { createPermissionGateExtension } from './extensions/permission-gate.js';
|
|
23
25
|
import { createSubagentExtension } from './extensions/subagent.js';
|
|
24
26
|
import { createTerminalTitleExtension } from './extensions/terminal-title.js';
|
|
@@ -46,6 +48,15 @@ export async function runAgent(opts) {
|
|
|
46
48
|
// Mirrors `mlx launch claude`: chunked paged prefill keeps long-prompt
|
|
47
49
|
// TTFT bounded on the default paged path.
|
|
48
50
|
process.env.MLX_PAGED_PREFILL_CHUNK_SIZE ??= '2048';
|
|
51
|
+
// Hard offline invariant — NOT a user-overridable default, hence `=` not `??=`.
|
|
52
|
+
// `mlx agent` is local-only: no cloud provider may ever be contacted. pi 0.81.1's
|
|
53
|
+
// interactive and RPC startup call `ModelRuntime.refresh()`, which when PI_OFFLINE
|
|
54
|
+
// is unset fetches remote provider catalogs from pi.dev and can refresh a persisted
|
|
55
|
+
// cloud credential — a network path the mlx-only prototype filter does NOT cover
|
|
56
|
+
// (it patches the read methods, not `refresh`). Forcing PI_OFFLINE=1 here, before pi
|
|
57
|
+
// is imported below, also pins every ModelRuntime in this process to `allowNetwork`
|
|
58
|
+
// off, so no ambient/prior cloud credential can leak outbound traffic.
|
|
59
|
+
process.env.PI_OFFLINE = '1';
|
|
49
60
|
// Force every paged-capable agent family through an isolated config clone.
|
|
50
61
|
// This includes quantized LFM2 (whose standalone default is deliberately
|
|
51
62
|
// flat) and Qwen3.5 dense/MoE (whose text-only defaults are flat). Gemma4's
|
|
@@ -55,19 +66,23 @@ export async function runAgent(opts) {
|
|
|
55
66
|
const preserveEmbeddedGemmaDraft = agentGemmaDraftEnabled();
|
|
56
67
|
const pagedConfigOverrides = opts.pagedConfigOverrides ?? new PagedConfigOverrideManager({ preserveEmbeddedGemmaDraft });
|
|
57
68
|
const modelHost = new MlxModelHost(opts.models.map((model) => model.discovered), {
|
|
58
|
-
resolveModelPathFn: (model) => pagedConfigOverrides.resolve(model.path, model.modelType),
|
|
69
|
+
resolveModelPathFn: (model, policy) => pagedConfigOverrides.resolve(model.path, model.modelType, policy?.persistPagedCache),
|
|
59
70
|
requirePagedCache: true,
|
|
71
|
+
persistPagedCache: opts.persistPagedCache,
|
|
60
72
|
});
|
|
61
|
-
// Keep the pi import strictly behind the seam. The seam carries BOTH main
|
|
62
|
-
//
|
|
63
|
-
// installation/lifecycle instead of being able to bypass it accidentally.
|
|
73
|
+
// Keep the pi import strictly behind the seam. The seam carries BOTH main and
|
|
74
|
+
// the ModelRuntime class, so tests and production exercise the same policy
|
|
75
|
+
// installation/lifecycle instead of being able to bypass it accidentally. The
|
|
76
|
+
// filter patches the runtime prototype (not the extension-only ModelRegistry
|
|
77
|
+
// facade), which is where the selector / listing / resolution paths read.
|
|
64
78
|
const pi = opts.piImpl ?? (await import('@earendil-works/pi-coding-agent'));
|
|
65
|
-
const restoreModelRegistry = installMlxOnlyModelRegistryFilter(pi.
|
|
79
|
+
const restoreModelRegistry = installMlxOnlyModelRegistryFilter(pi.ModelRuntime, opts.models.map((model) => model.discovered.name));
|
|
66
80
|
const subagentsEnabled = opts.models.length > 0 && !opts.argv.includes('--no-extensions') && !opts.argv.includes('-ne');
|
|
67
81
|
try {
|
|
68
82
|
await pi.main(opts.argv, {
|
|
69
83
|
extensionFactories: [
|
|
70
84
|
createMlxProviderExtension(opts.models, modelHost),
|
|
85
|
+
createLocalImageInputExtension(),
|
|
71
86
|
createPermissionGateExtension(),
|
|
72
87
|
...(subagentsEnabled ? [createSubagentExtension()] : []),
|
|
73
88
|
...(opts.traceLogFile !== undefined ? [createTraceNoticeExtension(opts.traceLogFile)] : []),
|
|
@@ -81,6 +96,17 @@ export async function runAgent(opts) {
|
|
|
81
96
|
}
|
|
82
97
|
finally {
|
|
83
98
|
await pagedConfigOverrides.cleanup();
|
|
99
|
+
// `mlx agent -p` is one-shot: flush any accepted cold-tier prefix blocks
|
|
100
|
+
// to disk before the process exits, otherwise a prompt's just-persisted
|
|
101
|
+
// KV could still be queued/mid-write when we return. No-op when the tier
|
|
102
|
+
// was never opened, bounded so a stuck fsync can't hang exit, and never
|
|
103
|
+
// allowed to throw out of cleanup (best-effort durability).
|
|
104
|
+
try {
|
|
105
|
+
coldCacheDrain(5000);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Best-effort: a drain failure must never mask the real exit path.
|
|
109
|
+
}
|
|
84
110
|
}
|
|
85
111
|
}
|
|
86
112
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mlx-node/agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"homepage": "https://github.com/mlx-node/mlx-node",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/mlx-node/mlx-node/issues"
|
|
@@ -21,16 +21,21 @@
|
|
|
21
21
|
".": {
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"import": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./catalog": {
|
|
26
|
+
"types": "./dist/catalog.d.ts",
|
|
27
|
+
"default": "./dist/catalog.js"
|
|
24
28
|
}
|
|
25
29
|
},
|
|
26
30
|
"scripts": {
|
|
27
31
|
"build": "tsc -b"
|
|
28
32
|
},
|
|
29
33
|
"dependencies": {
|
|
30
|
-
"@earendil-works/pi-ai": "0.
|
|
31
|
-
"@earendil-works/pi-coding-agent": "0.
|
|
32
|
-
"@mlx-node/
|
|
33
|
-
"@mlx-node/
|
|
34
|
+
"@earendil-works/pi-ai": "0.81.1",
|
|
35
|
+
"@earendil-works/pi-coding-agent": "0.81.1",
|
|
36
|
+
"@mlx-node/core": "0.0.9",
|
|
37
|
+
"@mlx-node/lm": "0.0.9",
|
|
38
|
+
"@mlx-node/server": "0.0.9",
|
|
34
39
|
"typebox": "1.3.6"
|
|
35
40
|
},
|
|
36
41
|
"devDependencies": {
|