@alisio/core 0.5.1 → 0.6.0
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/analysis/janitor.d.ts +48 -0
- package/dist/analysis/janitor.js +170 -18
- package/dist/analysis/jobs.d.ts +11 -2
- package/dist/analysis/jobs.js +13 -0
- package/dist/analysis/oci.d.ts +11 -2
- package/dist/analysis/oci.js +20 -4
- package/dist/analysis/python/sources.js +2 -1
- package/dist/analysis/rerun.d.ts +1 -1
- package/dist/analysis/status.d.ts +3 -1
- package/dist/analysis/status.js +3 -0
- package/dist/application.d.ts +38 -2
- package/dist/application.js +213 -4
- package/dist/changelog/data.js +64 -0
- package/dist/config.d.ts +110 -2
- package/dist/config.js +188 -6
- package/dist/core/call-internals.d.ts +22 -0
- package/dist/core/call-internals.js +9 -0
- package/dist/core/contracts.d.ts +2 -2
- package/dist/core/runner.js +111 -38
- package/dist/forge/broker.d.ts +51 -0
- package/dist/forge/broker.js +244 -0
- package/dist/forge/budget.d.ts +27 -0
- package/dist/forge/budget.js +60 -0
- package/dist/forge/catalog-view.d.ts +24 -0
- package/dist/forge/catalog-view.js +43 -0
- package/dist/forge/errors.d.ts +26 -0
- package/dist/forge/errors.js +49 -0
- package/dist/forge/guides/javascript.d.ts +25 -0
- package/dist/forge/guides/javascript.js +90 -0
- package/dist/forge/guides/provider.d.ts +42 -0
- package/dist/forge/guides/provider.js +96 -0
- package/dist/forge/guides/python-environment.d.ts +15 -0
- package/dist/forge/guides/python-environment.js +81 -0
- package/dist/forge/guides/python.d.ts +38 -0
- package/dist/forge/guides/python.js +170 -0
- package/dist/forge/javascript/engine.d.ts +44 -0
- package/dist/forge/javascript/engine.js +229 -0
- package/dist/forge/javascript/hosts/factory.d.ts +25 -0
- package/dist/forge/javascript/hosts/factory.js +21 -0
- package/dist/forge/javascript/hosts/framing.d.ts +17 -0
- package/dist/forge/javascript/hosts/framing.js +49 -0
- package/dist/forge/javascript/hosts/isolate-main.d.ts +9 -0
- package/dist/forge/javascript/hosts/isolate-main.js +63 -0
- package/dist/forge/javascript/hosts/process-entry.d.ts +1 -0
- package/dist/forge/javascript/hosts/process-entry.js +3 -0
- package/dist/forge/javascript/hosts/process-host.d.ts +33 -0
- package/dist/forge/javascript/hosts/process-host.js +160 -0
- package/dist/forge/javascript/hosts/worker-entry.d.ts +1 -0
- package/dist/forge/javascript/hosts/worker-entry.js +3 -0
- package/dist/forge/javascript/hosts/worker-host.d.ts +18 -0
- package/dist/forge/javascript/hosts/worker-host.js +101 -0
- package/dist/forge/javascript/prelude.d.ts +2 -0
- package/dist/forge/javascript/prelude.js +333 -0
- package/dist/forge/javascript/probe.d.ts +2 -0
- package/dist/forge/javascript/probe.js +53 -0
- package/dist/forge/javascript/runtime-manifest.d.ts +14 -0
- package/dist/forge/javascript/runtime-manifest.js +14 -0
- package/dist/forge/javascript/vm-runner.d.ts +21 -0
- package/dist/forge/javascript/vm-runner.js +301 -0
- package/dist/forge/javascript/wasm-asset.d.ts +8 -0
- package/dist/forge/javascript/wasm-asset.js +8 -0
- package/dist/forge/javascript/wasm.d.ts +15 -0
- package/dist/forge/javascript/wasm.js +54 -0
- package/dist/forge/journal.d.ts +105 -0
- package/dist/forge/journal.js +174 -0
- package/dist/forge/kernel.d.ts +76 -0
- package/dist/forge/kernel.js +338 -0
- package/dist/forge/limits.d.ts +38 -0
- package/dist/forge/limits.js +73 -0
- package/dist/forge/nested.d.ts +96 -0
- package/dist/forge/nested.js +214 -0
- package/dist/forge/ports.d.ts +120 -0
- package/dist/forge/ports.js +1 -0
- package/dist/forge/progress.d.ts +24 -0
- package/dist/forge/progress.js +76 -0
- package/dist/forge/protocol.d.ts +110 -0
- package/dist/forge/protocol.js +171 -0
- package/dist/forge/python/backend-selector.d.ts +38 -0
- package/dist/forge/python/backend-selector.js +111 -0
- package/dist/forge/python/backend.d.ts +89 -0
- package/dist/forge/python/backend.js +1 -0
- package/dist/forge/python/engine.d.ts +84 -0
- package/dist/forge/python/engine.js +279 -0
- package/dist/forge/python/frame-parser.d.ts +24 -0
- package/dist/forge/python/frame-parser.js +107 -0
- package/dist/forge/python/frame-transport.d.ts +40 -0
- package/dist/forge/python/frame-transport.js +101 -0
- package/dist/forge/python/import-scan.d.ts +22 -0
- package/dist/forge/python/import-scan.js +133 -0
- package/dist/forge/python/managed-backend.d.ts +30 -0
- package/dist/forge/python/managed-backend.js +111 -0
- package/dist/forge/python/oci-backend.d.ts +27 -0
- package/dist/forge/python/oci-backend.js +84 -0
- package/dist/forge/python/pyodide-backend.d.ts +40 -0
- package/dist/forge/python/pyodide-backend.js +289 -0
- package/dist/forge/python/pyodide-install.d.ts +41 -0
- package/dist/forge/python/pyodide-install.js +167 -0
- package/dist/forge/python/pyodide-manifest.d.ts +42 -0
- package/dist/forge/python/pyodide-manifest.js +376 -0
- package/dist/forge/python/pyodide-runner-source.d.ts +2 -0
- package/dist/forge/python/pyodide-runner-source.js +3 -0
- package/dist/forge/python/rss.d.ts +2 -0
- package/dist/forge/python/rss.js +33 -0
- package/dist/forge/python/scratch-copy.d.ts +6 -0
- package/dist/forge/python/scratch-copy.js +61 -0
- package/dist/forge/python/settle.d.ts +6 -0
- package/dist/forge/python/settle.js +21 -0
- package/dist/forge/python/status.d.ts +8 -0
- package/dist/forge/python/status.js +15 -0
- package/dist/forge/python/wasm-node.d.ts +59 -0
- package/dist/forge/python/wasm-node.js +220 -0
- package/dist/forge/python/wiring.d.ts +39 -0
- package/dist/forge/python/wiring.js +47 -0
- package/dist/forge/registry.d.ts +26 -0
- package/dist/forge/registry.js +83 -0
- package/dist/forge/result.d.ts +58 -0
- package/dist/forge/result.js +86 -0
- package/dist/forge/routing-rule.d.ts +19 -0
- package/dist/forge/routing-rule.js +31 -0
- package/dist/forge/runtime.d.ts +64 -0
- package/dist/forge/runtime.js +88 -0
- package/dist/forge/scratch-files.d.ts +36 -0
- package/dist/forge/scratch-files.js +94 -0
- package/dist/forge/scratch-provider.d.ts +41 -0
- package/dist/forge/scratch-provider.js +79 -0
- package/dist/forge/scratch-quota.d.ts +8 -0
- package/dist/forge/scratch-quota.js +25 -0
- package/dist/forge/selector.d.ts +35 -0
- package/dist/forge/selector.js +51 -0
- package/dist/forge/session-dir.d.ts +59 -0
- package/dist/forge/session-dir.js +239 -0
- package/dist/forge/session-path.d.ts +31 -0
- package/dist/forge/session-path.js +61 -0
- package/dist/forge/slots.d.ts +16 -0
- package/dist/forge/slots.js +47 -0
- package/dist/forge/state-delta.d.ts +54 -0
- package/dist/forge/state-delta.js +99 -0
- package/dist/forge/state-machine.d.ts +20 -0
- package/dist/forge/state-machine.js +40 -0
- package/dist/forge/state-store.d.ts +25 -0
- package/dist/forge/state-store.js +50 -0
- package/dist/forge/task-log.d.ts +9 -0
- package/dist/forge/task-log.js +42 -0
- package/dist/forge/verdict.d.ts +20 -0
- package/dist/forge/verdict.js +28 -0
- package/dist/forge-isolate.d.ts +6 -0
- package/dist/forge-isolate.js +6 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/permissions/decide.d.ts +42 -0
- package/dist/permissions/decide.js +24 -0
- package/dist/resources/context.d.ts +2 -1
- package/dist/resources/context.js +2 -1
- package/dist/runtime/access.d.ts +34 -0
- package/dist/runtime/access.js +80 -0
- package/dist/runtime/process.d.ts +2 -1
- package/dist/runtime/process.js +5 -5
- package/dist/runtime/store.d.ts +11 -0
- package/dist/runtime/store.js +103 -0
- package/dist/tools/analysis.d.ts +25 -2
- package/dist/tools/analysis.js +315 -267
- package/dist/tools/execute.js +6 -7
- package/dist/tools/forge-guide.d.ts +6 -0
- package/dist/tools/forge-guide.js +60 -0
- package/dist/tools/js-run.d.ts +19 -0
- package/dist/tools/js-run.js +126 -0
- package/dist/tools/standard.d.ts +7 -1
- package/dist/tools/standard.js +44 -43
- package/package.json +7 -2
package/dist/analysis/rerun.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface RerunPlan {
|
|
|
16
16
|
manifest: unknown[];
|
|
17
17
|
extras: Array<"analysis" | "science">;
|
|
18
18
|
/** The runtime of the original execution (a rerun uses the configured runtime now). */
|
|
19
|
-
runtime: "managed" | "oci";
|
|
19
|
+
runtime: "managed" | "oci" | "wasm";
|
|
20
20
|
}
|
|
21
21
|
export type RerunPreparation = {
|
|
22
22
|
ok: true;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Settings page: mode, interpreter and extras, container engine (probed only when it matters),
|
|
4
4
|
* limits and retention. One shape (`AnalysisStatus` of the SDK) for every surface.
|
|
5
5
|
*/
|
|
6
|
-
import type { AnalysisStatus } from "@alisio/sdk";
|
|
6
|
+
import type { AnalysisStatus, EngineDescriptor } from "@alisio/sdk";
|
|
7
7
|
import type { Config } from "../config.ts";
|
|
8
8
|
import type { AnalysisJanitor } from "./janitor.ts";
|
|
9
9
|
import type { OciRuntime } from "./oci.ts";
|
|
@@ -14,6 +14,8 @@ export declare function analysisStatus(input: {
|
|
|
14
14
|
/** Builds the container runtime of the current settings (probed on demand). */
|
|
15
15
|
oci?: () => OciRuntime;
|
|
16
16
|
janitor?: Pick<AnalysisJanitor, "lastSweep">;
|
|
17
|
+
/** The live state of the Python backends (isolation, readiness, profiles); omitted = no table. */
|
|
18
|
+
backends?: (signal?: AbortSignal) => Promise<EngineDescriptor[]>;
|
|
17
19
|
/** python_run is registered (analysis enabled and not `--read-only`). */
|
|
18
20
|
enabled: boolean;
|
|
19
21
|
readOnly: boolean;
|
package/dist/analysis/status.js
CHANGED
|
@@ -40,10 +40,13 @@ export async function analysisStatus(input) {
|
|
|
40
40
|
oci.reason = status.reason;
|
|
41
41
|
}
|
|
42
42
|
const lastSweep = await input.janitor?.lastSweep();
|
|
43
|
+
const backends = input.backends ? await input.backends(input.signal) : undefined;
|
|
43
44
|
return {
|
|
44
45
|
enabled: input.enabled,
|
|
45
46
|
readOnly: input.readOnly,
|
|
46
47
|
mode: config.runtime,
|
|
48
|
+
isolation: config.isolation,
|
|
49
|
+
...(backends ? { backends } : {}),
|
|
47
50
|
python,
|
|
48
51
|
oci,
|
|
49
52
|
limits: { timeoutMs: config.limits.timeoutMs },
|
package/dist/application.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AvailableProviderModel, ModelInfo, ModelProvider, Plugin, PluginCa
|
|
|
2
2
|
import { AgentDefinitionService } from "./agents/service.ts";
|
|
3
3
|
import { CapabilityGrants } from "./analysis/capabilities.ts";
|
|
4
4
|
import { DatasetService } from "./analysis/data/datasets.ts";
|
|
5
|
-
import { AnalysisJanitor } from "./analysis/janitor.ts";
|
|
5
|
+
import { AnalysisJanitor, ScratchJanitor } from "./analysis/janitor.ts";
|
|
6
6
|
import { AnalysisJobs } from "./analysis/jobs.ts";
|
|
7
7
|
import { OciRuntime } from "./analysis/oci.ts";
|
|
8
8
|
import { AnalysisRerun } from "./analysis/rerun.ts";
|
|
@@ -15,6 +15,8 @@ import type { ApprovalHandler } from "./core/contracts.ts";
|
|
|
15
15
|
import { ToolRegistry } from "./core/registry.ts";
|
|
16
16
|
import { AgentRunner } from "./core/runner.ts";
|
|
17
17
|
import { DecisionMetrics, DecisionRegistry, DecisionService } from "./decisions/index.ts";
|
|
18
|
+
import { SqliteExecutionJournal } from "./forge/journal.ts";
|
|
19
|
+
import { SqliteStateStore } from "./forge/state-store.ts";
|
|
18
20
|
import { GoalService } from "./goal/service.ts";
|
|
19
21
|
import { HerdrBridge } from "./integrations/herdr.ts";
|
|
20
22
|
import { McpConnector } from "./mcp/connector.ts";
|
|
@@ -188,12 +190,36 @@ export declare function createApplication(options?: AppOptions): Promise<{
|
|
|
188
190
|
jobs: AnalysisJobs;
|
|
189
191
|
/** Container runtime of the current configuration (status, pull); mode `managed` ignores it. */
|
|
190
192
|
oci: {
|
|
191
|
-
mode: () => "managed" | "oci";
|
|
193
|
+
mode: () => "auto" | "managed" | "oci" | "wasm";
|
|
192
194
|
runtime: () => OciRuntime;
|
|
193
195
|
};
|
|
196
|
+
/** The live state of every Python backend (isolation, ready, profiles, reason). */
|
|
197
|
+
backends: (signal?: AbortSignal) => Promise<import("@alisio/sdk").EngineDescriptor[]>;
|
|
194
198
|
rerun: AnalysisRerun;
|
|
195
199
|
janitor: AnalysisJanitor;
|
|
196
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* Forja services for the engines (Phase 3b): the execution journal, the cross-execution
|
|
203
|
+
* state, the scratchpad of a session (undefined while the JavaScript engine is off) and the
|
|
204
|
+
* retention sweep of the volatile session folders.
|
|
205
|
+
*/
|
|
206
|
+
forge: {
|
|
207
|
+
journal: SqliteExecutionJournal;
|
|
208
|
+
state: SqliteStateStore;
|
|
209
|
+
/** The scratchpad, created on first use; under the same gate as the write root. */
|
|
210
|
+
scratchpad: ((session: string) => Promise<string | undefined>) | undefined;
|
|
211
|
+
/**
|
|
212
|
+
* What the HTTP surface sees (L2): `available` follows the gate of the write root (engine
|
|
213
|
+
* verified, not `--read-only`); the folder is only looked up, never created.
|
|
214
|
+
*/
|
|
215
|
+
scratchpadView: ((session: string) => Promise<{
|
|
216
|
+
available: boolean;
|
|
217
|
+
path?: string;
|
|
218
|
+
}>) | undefined;
|
|
219
|
+
scratchJanitor: ScratchJanitor;
|
|
220
|
+
/** The JavaScript engine: descriptor and effective limits (`alisio forge status`), probe. */
|
|
221
|
+
runtime: import("./forge/runtime.ts").ForgeRuntime;
|
|
222
|
+
};
|
|
197
223
|
/** Keys a project layer set that only the user layer decides (ignored on load). */
|
|
198
224
|
configDiagnostics: string[];
|
|
199
225
|
/** Tabular datasets of every session (ingestion, read-only queries, pages). */
|
|
@@ -320,6 +346,16 @@ export declare function createApplication(options?: AppOptions): Promise<{
|
|
|
320
346
|
resolveModel: (reference: string, signal?: AbortSignal) => Promise<ResolvedProviderModel>;
|
|
321
347
|
/** Creates and binds a fresh session to an optional configured model selector. */
|
|
322
348
|
createSession(reference?: string): Promise<import("./index.ts").Session>;
|
|
349
|
+
/**
|
|
350
|
+
* Re-binds an existing session to the provider that owns `model` (a configured profile), else
|
|
351
|
+
* to the active provider. The session keeps a single provider+model binding, so choosing a
|
|
352
|
+
* model of the now-active provider makes it usable even when the session's original provider
|
|
353
|
+
* is gone. The persisted transcript is provider-neutral: no new session is needed.
|
|
354
|
+
*/
|
|
355
|
+
rebindSession(sessionId: string, model?: string): Promise<{
|
|
356
|
+
provider: string;
|
|
357
|
+
model: string;
|
|
358
|
+
}>;
|
|
323
359
|
/** Persists and activates only after construction succeeds; programmatic overrides stay fixed. */
|
|
324
360
|
activateProvider(id: string, profile: Record<string, import("@alisio/sdk").ProviderConfigurationValue>, credentials: Record<string, string>, model: string, profileName?: string): Promise<void>;
|
|
325
361
|
/** Activates a stored profile without exposing its credentials. Returns false for a no-op. */
|
package/dist/application.js
CHANGED
|
@@ -4,7 +4,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
4
4
|
import { AgentDefinitionService } from "./agents/service.js";
|
|
5
5
|
import { CapabilityGrants } from "./analysis/capabilities.js";
|
|
6
6
|
import { DatasetService } from "./analysis/data/datasets.js";
|
|
7
|
-
import { AnalysisJanitor } from "./analysis/janitor.js";
|
|
7
|
+
import { AnalysisJanitor, ScratchJanitor } from "./analysis/janitor.js";
|
|
8
8
|
import { AnalysisJobs } from "./analysis/jobs.js";
|
|
9
9
|
import { OciRuntime } from "./analysis/oci.js";
|
|
10
10
|
import { AnalysisRerun } from "./analysis/rerun.js";
|
|
@@ -21,6 +21,12 @@ import { DEFAULT_MAX_OUTPUT_TOKENS } from "./core/output-limit.js";
|
|
|
21
21
|
import { ToolRegistry } from "./core/registry.js";
|
|
22
22
|
import { AgentRunner } from "./core/runner.js";
|
|
23
23
|
import { bindDecisions, DecisionMetrics, DecisionRegistry, DecisionService, } from "./decisions/index.js";
|
|
24
|
+
import { reconcileInterruptedExecutions, SqliteExecutionJournal } from "./forge/journal.js";
|
|
25
|
+
import { describePythonBackends } from "./forge/python/status.js";
|
|
26
|
+
import { createPythonBackends } from "./forge/python/wiring.js";
|
|
27
|
+
import { createForgeRuntime } from "./forge/runtime.js";
|
|
28
|
+
import { createScratchpadLookup, createScratchpadProvider, scratchSweepRoots, } from "./forge/scratch-provider.js";
|
|
29
|
+
import { SqliteStateStore } from "./forge/state-store.js";
|
|
24
30
|
import { GoalService } from "./goal/service.js";
|
|
25
31
|
import { GoalStore } from "./goal/store.js";
|
|
26
32
|
import { HerdrBridge } from "./integrations/herdr.js";
|
|
@@ -35,7 +41,7 @@ import { Skills, skillRoots } from "./resources/skills.js";
|
|
|
35
41
|
import { PathAccess } from "./runtime/access.js";
|
|
36
42
|
import { BlobStore } from "./runtime/blobs.js";
|
|
37
43
|
import { isPathSpec } from "./runtime/modules.js";
|
|
38
|
-
import { findWorkspace } from "./runtime/paths.js";
|
|
44
|
+
import { findWorkspace, workspaceKey } from "./runtime/paths.js";
|
|
39
45
|
import { SQLiteStore } from "./runtime/store.js";
|
|
40
46
|
import { ChildSessions } from "./sessions/children.js";
|
|
41
47
|
import { SideQuestions } from "./sessions/side-questions.js";
|
|
@@ -100,6 +106,24 @@ export async function createApplication(options = {}) {
|
|
|
100
106
|
}
|
|
101
107
|
});
|
|
102
108
|
const askExternalDirectory = options.approveExternalDirectory;
|
|
109
|
+
const customStore = !!options.db;
|
|
110
|
+
const scratchStateRoot = options.db && options.db !== ":memory:" ? dirname(options.db) : stateHome();
|
|
111
|
+
// Forja's scratchpad (spec §9.4): an authorized write root only while the JavaScript engine is
|
|
112
|
+
// enabled, so a build with Forja off behaves exactly as before. `store` is read lazily, on the
|
|
113
|
+
// first path outside the workspace.
|
|
114
|
+
const scratchOptions = {
|
|
115
|
+
rootOf: (session) => store.rootOf(session),
|
|
116
|
+
workspaceOf: (rootSessionId) => store.get(rootSessionId).workspace,
|
|
117
|
+
workspaceKey,
|
|
118
|
+
stateHome: scratchStateRoot,
|
|
119
|
+
configRoot: () => config.forge.scratch.root,
|
|
120
|
+
};
|
|
121
|
+
const scratchEnabled = config.forge.javascript.enabled && config.forge.scratch.enabled;
|
|
122
|
+
const scratchpad = scratchEnabled ? createScratchpadProvider(scratchOptions) : undefined;
|
|
123
|
+
const scratchpadLookup = scratchEnabled ? createScratchpadLookup(scratchOptions) : undefined;
|
|
124
|
+
// The scratchpad is a free write root only once the engine verified itself (`jsReady`, known after
|
|
125
|
+
// `createForgeRuntime` below): a failed self-check leaves it an ordinary directory (L9).
|
|
126
|
+
let scratchReady = false;
|
|
103
127
|
const pathAccess = new PathAccess({
|
|
104
128
|
workspace,
|
|
105
129
|
extraRoots: [...config.additionalDirectories, ...addDirs],
|
|
@@ -109,8 +133,20 @@ export async function createApplication(options = {}) {
|
|
|
109
133
|
}
|
|
110
134
|
: {}),
|
|
111
135
|
readOnly: !!options.readOnly,
|
|
136
|
+
...(scratchpad
|
|
137
|
+
? {
|
|
138
|
+
scratchpad: (session) => scratchReady ? scratchpad(session) : Promise.resolve(undefined),
|
|
139
|
+
scratchMaxBytes: () => config.forge.scratch.maxBytes,
|
|
140
|
+
}
|
|
141
|
+
: {}),
|
|
112
142
|
});
|
|
113
143
|
const store = new SQLiteStore(options.db ?? join(stateHome(), "sessions.sqlite"));
|
|
144
|
+
// Executions of a process that is gone cannot be running: close them (nothing is relaunched).
|
|
145
|
+
reconcileInterruptedExecutions(store.db);
|
|
146
|
+
const forgeJournal = new SqliteExecutionJournal(store.db);
|
|
147
|
+
const forgeState = new SqliteStateStore(store.db, {
|
|
148
|
+
maxTotalBytes: () => config.forge.state.maxTotalBytes,
|
|
149
|
+
});
|
|
114
150
|
// Content-addressed attachment bytes next to the session database (no I/O until `put`).
|
|
115
151
|
const blobs = new BlobStore({
|
|
116
152
|
root: options.db && options.db !== ":memory:"
|
|
@@ -352,6 +388,14 @@ export async function createApplication(options = {}) {
|
|
|
352
388
|
}));
|
|
353
389
|
context.extras.push(async () => skills.catalog(), ...plugins.contexts);
|
|
354
390
|
const { registerStandard } = await import("./tools/standard.js");
|
|
391
|
+
// Forja's JavaScript engine verifies itself here (the WASM against its pinned hash). Enabled
|
|
392
|
+
// means `js_run` replaces `execute` for good: a failed check leaves neither (no fallback).
|
|
393
|
+
const forgeRuntime = await createForgeRuntime({
|
|
394
|
+
forge: () => config.forge,
|
|
395
|
+
journal: forgeJournal,
|
|
396
|
+
state: forgeState,
|
|
397
|
+
});
|
|
398
|
+
scratchReady = forgeRuntime.jsReady;
|
|
355
399
|
// `runner` is referenced lazily (constructed just below): safe because these accessors only
|
|
356
400
|
// run once the tool is actually called, well after the runner exists.
|
|
357
401
|
registerStandard(registry, workspace, skills, context, plugins.ui, {
|
|
@@ -359,7 +403,27 @@ export async function createApplication(options = {}) {
|
|
|
359
403
|
}, {
|
|
360
404
|
config: config.websearch,
|
|
361
405
|
resolveExtension: () => plugins.extensions.resolve("websearch"),
|
|
362
|
-
}, pathAccess);
|
|
406
|
+
}, pathAccess, { execute: !config.forge.javascript.enabled });
|
|
407
|
+
// `--read-only` has no scratchpad root, so `codePath` is unavailable there.
|
|
408
|
+
const sessionScratchpad = options.readOnly ? async () => undefined : scratchpad;
|
|
409
|
+
if (forgeRuntime.jsReady) {
|
|
410
|
+
const { registerJsRun } = await import("./tools/js-run.js");
|
|
411
|
+
const { buildRoutingRule } = await import("./forge/routing-rule.js");
|
|
412
|
+
registerJsRun(registry, {
|
|
413
|
+
kernel: forgeRuntime.kernel,
|
|
414
|
+
rootOf: (session) => store.rootOf(session),
|
|
415
|
+
scratchpad: async (session) => sessionScratchpad?.(session),
|
|
416
|
+
policy: () => runner.policy,
|
|
417
|
+
});
|
|
418
|
+
// Layer 0 of the context (spec §8.1): present only because `js_run` is, per session.
|
|
419
|
+
context.extras.push(async (sessionId) => buildRoutingRule({
|
|
420
|
+
javascript: true,
|
|
421
|
+
python: registry.list().some((tool) => tool.name === "python_run"),
|
|
422
|
+
// Registered later (after `python_run`), so it is looked up when the prompt is built.
|
|
423
|
+
guide: registry.list().some((tool) => tool.name === "forge_guide"),
|
|
424
|
+
scratchpad: sessionId ? await sessionScratchpad?.(sessionId) : undefined,
|
|
425
|
+
}));
|
|
426
|
+
}
|
|
363
427
|
// Python analysis and artifacts live next to the session database (like attachment blobs).
|
|
364
428
|
const artifacts = new ArtifactStore({
|
|
365
429
|
root: stateRoot,
|
|
@@ -395,6 +459,13 @@ export async function createApplication(options = {}) {
|
|
|
395
459
|
return ociRuntime.runtime;
|
|
396
460
|
},
|
|
397
461
|
};
|
|
462
|
+
// The Python backends as surfaces report them (`GET /api/analysis`, `GET /api/forge`); the tool
|
|
463
|
+
// builds its own for running. Read live: the settings can change between calls.
|
|
464
|
+
const pythonBackends = createPythonBackends({
|
|
465
|
+
runtime: analysisRuntime,
|
|
466
|
+
oci,
|
|
467
|
+
wasm: { stateRoot, settings: () => config.analysis.wasm },
|
|
468
|
+
});
|
|
398
469
|
// Tabular datasets (one SQLite file each); XLSX needs the discovered Python.
|
|
399
470
|
const datasets = new DatasetService({
|
|
400
471
|
root: stateRoot,
|
|
@@ -416,6 +487,28 @@ export async function createApplication(options = {}) {
|
|
|
416
487
|
? { initialDelayMs: options.analysisSweepDelayMs }
|
|
417
488
|
: {}),
|
|
418
489
|
});
|
|
490
|
+
// Volatile Forja session folders: same schedule as the analysis sweep, gone with their session.
|
|
491
|
+
const scratchJanitor = new ScratchJanitor({
|
|
492
|
+
stateRoot,
|
|
493
|
+
roots: () => scratchSweepRoots({
|
|
494
|
+
customStore,
|
|
495
|
+
stateHome: scratchStateRoot,
|
|
496
|
+
configRoot: config.forge.scratch.root,
|
|
497
|
+
}),
|
|
498
|
+
sessionExists: (rootSessionId) => {
|
|
499
|
+
try {
|
|
500
|
+
store.get(rootSessionId);
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
retentionDays: () => config.forge.scratch.retentionDays,
|
|
508
|
+
...(options.analysisSweepDelayMs !== undefined
|
|
509
|
+
? { initialDelayMs: options.analysisSweepDelayMs }
|
|
510
|
+
: {}),
|
|
511
|
+
});
|
|
419
512
|
// Background tasks: a service over the shared database (state change = compare-and-set), logs
|
|
420
513
|
// under `<state>/tasks/`. Tasks of a dead owner process (a previous run) become `lost` now.
|
|
421
514
|
const tasks = new BackgroundTasks({
|
|
@@ -473,6 +566,15 @@ export async function createApplication(options = {}) {
|
|
|
473
566
|
runtime: analysisRuntime,
|
|
474
567
|
datasets,
|
|
475
568
|
oci,
|
|
569
|
+
// Read live: the policy and the Pyodide settings are global-only and may change.
|
|
570
|
+
isolation: () => config.analysis.isolation,
|
|
571
|
+
wasm: { stateRoot, settings: () => config.analysis.wasm },
|
|
572
|
+
// Python runs through the same kernel as `js_run`: one journal, one state store.
|
|
573
|
+
forge: { kernel: forgeRuntime.kernel, engines: forgeRuntime.engines },
|
|
574
|
+
// `codePath` and the read-only exposure exist only where `js_run` has its scratchpad too.
|
|
575
|
+
...(scratchReady && sessionScratchpad
|
|
576
|
+
? { scratchpad: async (session) => sessionScratchpad(session) }
|
|
577
|
+
: {}),
|
|
476
578
|
rerun: analysisRerun,
|
|
477
579
|
rootOf: (id) => store.rootOf(id),
|
|
478
580
|
// Read live: a changed `analysis.limits.*` setting applies to the next call.
|
|
@@ -485,6 +587,8 @@ export async function createApplication(options = {}) {
|
|
|
485
587
|
},
|
|
486
588
|
},
|
|
487
589
|
startup: { candidates: candidates.length > 0, extras: active?.extras ?? [] },
|
|
590
|
+
// Error results point at `forge_guide` only while that tool exists (spec §8.4).
|
|
591
|
+
guideHints: () => registry.list().some((tool) => tool.name === "forge_guide"),
|
|
488
592
|
});
|
|
489
593
|
const { registerArtifactTools } = await import("./tools/artifacts.js");
|
|
490
594
|
registerArtifactTools(registry, { store: artifacts, rootOf: (id) => store.rootOf(id) });
|
|
@@ -496,6 +600,42 @@ export async function createApplication(options = {}) {
|
|
|
496
600
|
context.extras.push(async () => DASHBOARD_PROMPT_RULE);
|
|
497
601
|
}
|
|
498
602
|
}
|
|
603
|
+
// Layer 2 of the context (spec §8.3): `forge_guide`. Registered only with Forja on and a ready
|
|
604
|
+
// engine for at least one language; the `python.*` topics need `python_run`.
|
|
605
|
+
if (config.forge.guides.enabled && config.forge.javascript.enabled) {
|
|
606
|
+
const [{ GuideProvider }, { registerForgeGuide }, { probePythonEnvironment }] = await Promise.all([
|
|
607
|
+
import("./forge/guides/provider.js"),
|
|
608
|
+
import("./tools/forge-guide.js"),
|
|
609
|
+
import("./forge/guides/python-environment.js"),
|
|
610
|
+
]);
|
|
611
|
+
const hasPython = registry.list().some((tool) => tool.name === "python_run");
|
|
612
|
+
registerForgeGuide(registry, new GuideProvider({
|
|
613
|
+
...(forgeRuntime.jsReady
|
|
614
|
+
? {
|
|
615
|
+
javascript: async () => {
|
|
616
|
+
const status = await forgeRuntime.status();
|
|
617
|
+
return {
|
|
618
|
+
version: status.javascript.version,
|
|
619
|
+
isolation: status.engines[0]?.isolation ?? "wasm-isolate",
|
|
620
|
+
host: status.javascript.host,
|
|
621
|
+
limits: status.javascript.limits,
|
|
622
|
+
};
|
|
623
|
+
},
|
|
624
|
+
}
|
|
625
|
+
: {}),
|
|
626
|
+
...(hasPython
|
|
627
|
+
? {
|
|
628
|
+
python: () => probePythonEnvironment({
|
|
629
|
+
backends: pythonBackends,
|
|
630
|
+
runtime: () => config.analysis.runtime,
|
|
631
|
+
isolation: () => config.analysis.isolation,
|
|
632
|
+
managedExtras: async () => (await analysisRuntime.activeRuntime())?.extras ?? [],
|
|
633
|
+
smartDashboard: () => config.analysis.smartDashboard,
|
|
634
|
+
}),
|
|
635
|
+
}
|
|
636
|
+
: {}),
|
|
637
|
+
}));
|
|
638
|
+
}
|
|
499
639
|
// `exit_plan` (opt-in: only the plan agent's run sees it): the plan review hand-over.
|
|
500
640
|
const { registerExitPlan } = await import("./plan/exit-plan.js");
|
|
501
641
|
registerExitPlan(registry, {
|
|
@@ -957,8 +1097,10 @@ export async function createApplication(options = {}) {
|
|
|
957
1097
|
if (analysisEnabled && options.db !== ":memory:")
|
|
958
1098
|
janitor.start();
|
|
959
1099
|
// Retention is maintenance that deletes files: never under --read-only, never for `:memory:`.
|
|
960
|
-
if (options.db !== ":memory:" && !options.readOnly)
|
|
1100
|
+
if (options.db !== ":memory:" && !options.readOnly) {
|
|
961
1101
|
taskJanitor.start();
|
|
1102
|
+
scratchJanitor.start();
|
|
1103
|
+
}
|
|
962
1104
|
return {
|
|
963
1105
|
workspace,
|
|
964
1106
|
pathAccess,
|
|
@@ -983,9 +1125,39 @@ export async function createApplication(options = {}) {
|
|
|
983
1125
|
jobs: analysisJobs,
|
|
984
1126
|
/** Container runtime of the current configuration (status, pull); mode `managed` ignores it. */
|
|
985
1127
|
oci,
|
|
1128
|
+
/** The live state of every Python backend (isolation, ready, profiles, reason). */
|
|
1129
|
+
backends: (signal) => describePythonBackends(pythonBackends, signal),
|
|
986
1130
|
rerun: analysisRerun,
|
|
987
1131
|
janitor,
|
|
988
1132
|
},
|
|
1133
|
+
/**
|
|
1134
|
+
* Forja services for the engines (Phase 3b): the execution journal, the cross-execution
|
|
1135
|
+
* state, the scratchpad of a session (undefined while the JavaScript engine is off) and the
|
|
1136
|
+
* retention sweep of the volatile session folders.
|
|
1137
|
+
*/
|
|
1138
|
+
forge: {
|
|
1139
|
+
journal: forgeJournal,
|
|
1140
|
+
state: forgeState,
|
|
1141
|
+
/** The scratchpad, created on first use; under the same gate as the write root. */
|
|
1142
|
+
scratchpad: scratchpad
|
|
1143
|
+
? (session) => scratchReady && !options.readOnly ? scratchpad(session) : Promise.resolve(undefined)
|
|
1144
|
+
: undefined,
|
|
1145
|
+
/**
|
|
1146
|
+
* What the HTTP surface sees (L2): `available` follows the gate of the write root (engine
|
|
1147
|
+
* verified, not `--read-only`); the folder is only looked up, never created.
|
|
1148
|
+
*/
|
|
1149
|
+
scratchpadView: scratchpadLookup
|
|
1150
|
+
? async (session) => {
|
|
1151
|
+
if (!scratchReady || options.readOnly)
|
|
1152
|
+
return { available: false };
|
|
1153
|
+
const path = await scratchpadLookup(session);
|
|
1154
|
+
return path ? { available: true, path } : { available: true };
|
|
1155
|
+
}
|
|
1156
|
+
: undefined,
|
|
1157
|
+
scratchJanitor,
|
|
1158
|
+
/** The JavaScript engine: descriptor and effective limits (`alisio forge status`), probe. */
|
|
1159
|
+
runtime: forgeRuntime,
|
|
1160
|
+
},
|
|
989
1161
|
/** Keys a project layer set that only the user layer decides (ignored on load). */
|
|
990
1162
|
configDiagnostics: configProvenance.ignored ?? [],
|
|
991
1163
|
/** Tabular datasets of every session (ingestion, read-only queries, pages). */
|
|
@@ -1306,6 +1478,18 @@ export async function createApplication(options = {}) {
|
|
|
1306
1478
|
config.tasks = { ...config.tasks, [leaf]: Number(value) };
|
|
1307
1479
|
break;
|
|
1308
1480
|
}
|
|
1481
|
+
case "forge.javascript.enabled":
|
|
1482
|
+
// The tool set (`js_run` or `execute`) is chosen when the application starts: the
|
|
1483
|
+
// change applies to the next application (the web recycles the workspace application).
|
|
1484
|
+
config.forge = {
|
|
1485
|
+
...config.forge,
|
|
1486
|
+
javascript: { ...config.forge.javascript, enabled: value === true },
|
|
1487
|
+
};
|
|
1488
|
+
break;
|
|
1489
|
+
case "forge.guides.enabled":
|
|
1490
|
+
// Like `forge.javascript.enabled`: `forge_guide` is registered at start.
|
|
1491
|
+
config.forge = { ...config.forge, guides: { enabled: value === true } };
|
|
1492
|
+
break;
|
|
1309
1493
|
case "web.iconTheme":
|
|
1310
1494
|
// Read live by the icon-theme routes on the next request; `"none"` clears the theme.
|
|
1311
1495
|
config.web = { ...config.web, iconTheme: String(value) };
|
|
@@ -1400,6 +1584,30 @@ export async function createApplication(options = {}) {
|
|
|
1400
1584
|
sessionProviders.set(session.id, runtime);
|
|
1401
1585
|
return session;
|
|
1402
1586
|
},
|
|
1587
|
+
/**
|
|
1588
|
+
* Re-binds an existing session to the provider that owns `model` (a configured profile), else
|
|
1589
|
+
* to the active provider. The session keeps a single provider+model binding, so choosing a
|
|
1590
|
+
* model of the now-active provider makes it usable even when the session's original provider
|
|
1591
|
+
* is gone. The persisted transcript is provider-neutral: no new session is needed.
|
|
1592
|
+
*/
|
|
1593
|
+
async rebindSession(sessionId, model) {
|
|
1594
|
+
const requested = model?.trim();
|
|
1595
|
+
if (requested) {
|
|
1596
|
+
const target = await resolveModel(requested).catch(() => undefined);
|
|
1597
|
+
if (target) {
|
|
1598
|
+
const runtime = await runtimeFor(target);
|
|
1599
|
+
store.updateBinding(sessionId, runtime.id, target.model.id);
|
|
1600
|
+
sessionProviders.set(sessionId, runtime);
|
|
1601
|
+
return { provider: runtime.id, model: target.model.id };
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
// No configured profile owns it (or none requested): keep the active provider.
|
|
1605
|
+
const next = requested || provider.model;
|
|
1606
|
+
store.updateBinding(sessionId, provider.id, next);
|
|
1607
|
+
sessionProviders.set(sessionId, provider.currentProvider);
|
|
1608
|
+
retainedProviders.add(provider.currentProvider);
|
|
1609
|
+
return { provider: provider.id, model: next };
|
|
1610
|
+
},
|
|
1403
1611
|
/** Persists and activates only after construction succeeds; programmatic overrides stay fixed. */
|
|
1404
1612
|
async activateProvider(id, profile, credentials, model, profileName = id) {
|
|
1405
1613
|
if (options.provider)
|
|
@@ -1468,6 +1676,7 @@ export async function createApplication(options = {}) {
|
|
|
1468
1676
|
try {
|
|
1469
1677
|
janitor.stop();
|
|
1470
1678
|
taskJanitor.stop();
|
|
1679
|
+
scratchJanitor.stop();
|
|
1471
1680
|
datasets?.close();
|
|
1472
1681
|
// Tasks first in the same stage: they end with Alisio (process groups killed).
|
|
1473
1682
|
await capped(Promise.allSettled([herdr.close(), mcp.close(), tasks.close()]));
|
package/dist/changelog/data.js
CHANGED
|
@@ -2,6 +2,70 @@
|
|
|
2
2
|
export const CHANGELOG_DATA = {
|
|
3
3
|
schema: 1,
|
|
4
4
|
entries: [
|
|
5
|
+
{
|
|
6
|
+
version: "0.6.0",
|
|
7
|
+
date: "2026-10-12",
|
|
8
|
+
sections: [
|
|
9
|
+
{
|
|
10
|
+
title: "Added",
|
|
11
|
+
items: [
|
|
12
|
+
"`js_run`: the model writes a short JavaScript script that composes tools, runs them in parallel and returns a small result. It runs in an isolated QuickJS-WebAssembly VM with limits for time, memory, calls and output; every nested call passes your permissions and a script cannot ask for approval.",
|
|
13
|
+
"`forge_guide`: a short guide for `js_run` and `python_run`, generated from the live limits, tools and Python backend, so the model learns the real environment on demand (`forge.guides.enabled`).",
|
|
14
|
+
"`python_run` backends `auto`, `managed`, `oci` and `wasm`, with an isolation policy (`analysis.isolation`: `preferred`, `required` or `strict`). The new `wasm` backend runs Pyodide in a restricted Node process with no Docker (`alisio analysis setup --wasm`); the result names the engine and why it was chosen. `alisio forge status` shows every engine and why one is not ready.",
|
|
15
|
+
"A per-session scratchpad where the model keeps scripts and intermediate files and runs them with `codePath` (`forge.scratch.*`: 7 days of retention and a 256 MiB quota by default). Python sees it read only through `ALISIO_SCRATCH_DIR`.",
|
|
16
|
+
"Web UI: a Forja settings page, a `js_run` card with the code, the nested calls and a progress bar, an isolation badge and the reason on every `python_run`, and a Scratch tab to preview, download and empty the scratchpad. The terminal shows nested-call counts and the Python backend.",
|
|
17
|
+
"Database migrations v9 and v10 (the execution journal and the shared script state) and `alisio_runtime` 4 with `alisio_runtime.forge`; they apply automatically.",
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: "Changed",
|
|
22
|
+
items: [
|
|
23
|
+
"`analysis.runtime` defaults to `auto` (before: `managed`): Alisio uses the most isolated backend that is ready (container, then WebAssembly, then your Python). Set `managed` to keep the old behavior; an explicit value is honored or fails, never replaced.",
|
|
24
|
+
"While `forge.javascript.enabled` is on (the default), `execute` is not registered: `js_run` takes its place. Set it to `false` to get `execute` and the previous prompt back; saved sessions with `execute` calls still open.",
|
|
25
|
+
"The script's own progress now reaches the web UI next to the nested-call events instead of being replaced by them.",
|
|
26
|
+
"Isolation has limits you should know: the `wasm` backend does not block the network on Node 22 and 24, its memory limit is soft and a hostile script can reach the runner's JavaScript; `managed` is not a sandbox. The Forja documentation page lists them.",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
version: "0.5.2",
|
|
33
|
+
date: "2026-10-08",
|
|
34
|
+
sections: [
|
|
35
|
+
{
|
|
36
|
+
title: "Fixed",
|
|
37
|
+
items: [
|
|
38
|
+
"Changing the model in a chat re-binds the session to the model's provider, so you can pick any model of the current provider and keep using the same conversation even when the session's original provider is gone, without starting a new chat.",
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
version: "0.5.1",
|
|
45
|
+
date: "2026-10-08",
|
|
46
|
+
sections: [
|
|
47
|
+
{
|
|
48
|
+
title: "Fixed",
|
|
49
|
+
items: [
|
|
50
|
+
'The file panel previews PDFs again: the server no longer blocks the same-origin frame, which showed "refused to connect" instead of the document.',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
version: "0.5.0",
|
|
57
|
+
date: "2026-10-07",
|
|
58
|
+
sections: [
|
|
59
|
+
{
|
|
60
|
+
title: "Added",
|
|
61
|
+
items: [
|
|
62
|
+
"The Trajectory tab offers a Table/Timeline switch: the timeline is an execution trace with lanes for the model, the tools and the user waits, plus tokens per turn and time per tool.",
|
|
63
|
+
"The file panel adds a right-click menu to copy a file's absolute path, relative path or name, and previews PDFs and HTML (with a source toggle for HTML).",
|
|
64
|
+
"Icon-theme plugins: choose one in Settings, Appearance (for example the Material Icon Theme plugin).",
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
5
69
|
{
|
|
6
70
|
version: "0.4.5",
|
|
7
71
|
date: "2026-10-07",
|