@alisio/core 0.1.0-alpha.9 → 0.1.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/README.md +52 -7
- package/dist/agents/active.d.ts +99 -0
- package/dist/agents/active.js +218 -0
- package/dist/agents/capabilities.d.ts +26 -0
- package/dist/agents/capabilities.js +120 -0
- package/dist/agents/create-agent-skill.d.ts +11 -0
- package/dist/agents/create-agent-skill.js +60 -0
- package/dist/agents/definitions.d.ts +88 -0
- package/dist/agents/definitions.js +404 -0
- package/dist/agents/draft.d.ts +41 -0
- package/dist/agents/draft.js +133 -0
- package/dist/agents/service.d.ts +57 -0
- package/dist/agents/service.js +86 -0
- package/dist/agents/templates.d.ts +8 -0
- package/dist/agents/templates.js +102 -0
- package/dist/analysis/capabilities.d.ts +48 -0
- package/dist/analysis/capabilities.js +90 -0
- package/dist/analysis/data/csv.d.ts +46 -0
- package/dist/analysis/data/csv.js +240 -0
- package/dist/analysis/data/datasets.d.ts +132 -0
- package/dist/analysis/data/datasets.js +441 -0
- package/dist/analysis/data/describe.d.ts +27 -0
- package/dist/analysis/data/describe.js +103 -0
- package/dist/analysis/data/engine-client.d.ts +58 -0
- package/dist/analysis/data/engine-client.js +304 -0
- package/dist/analysis/data/engine-entry.d.ts +1 -0
- package/dist/analysis/data/engine-entry.js +59 -0
- package/dist/analysis/data/engine-source.d.ts +4 -0
- package/dist/analysis/data/engine-source.js +6 -0
- package/dist/analysis/data/engine-types.d.ts +86 -0
- package/dist/analysis/data/engine-types.js +7 -0
- package/dist/analysis/data/infer.d.ts +42 -0
- package/dist/analysis/data/infer.js +118 -0
- package/dist/analysis/data/ingest-worker.d.ts +17 -0
- package/dist/analysis/data/ingest-worker.js +365 -0
- package/dist/analysis/data/json.d.ts +19 -0
- package/dist/analysis/data/json.js +44 -0
- package/dist/analysis/data/query-worker.d.ts +12 -0
- package/dist/analysis/data/query-worker.js +75 -0
- package/dist/analysis/data/rows.d.ts +39 -0
- package/dist/analysis/data/rows.js +120 -0
- package/dist/analysis/data/sql-guard.d.ts +20 -0
- package/dist/analysis/data/sql-guard.js +129 -0
- package/dist/analysis/data/stats.d.ts +18 -0
- package/dist/analysis/data/stats.js +77 -0
- package/dist/analysis/install-hints.d.ts +33 -0
- package/dist/analysis/install-hints.js +144 -0
- package/dist/analysis/janitor.d.ts +60 -0
- package/dist/analysis/janitor.js +264 -0
- package/dist/analysis/jobs.d.ts +94 -0
- package/dist/analysis/jobs.js +165 -0
- package/dist/analysis/oci.d.ts +129 -0
- package/dist/analysis/oci.js +330 -0
- package/dist/analysis/python/sources.d.ts +2 -0
- package/dist/analysis/python/sources.js +10 -0
- package/dist/analysis/requirements.d.ts +32 -0
- package/dist/analysis/requirements.js +73 -0
- package/dist/analysis/rerun.d.ts +40 -0
- package/dist/analysis/rerun.js +136 -0
- package/dist/analysis/runtime-manager.d.ts +146 -0
- package/dist/analysis/runtime-manager.js +472 -0
- package/dist/analysis/status.d.ts +21 -0
- package/dist/analysis/status.js +52 -0
- package/dist/application.d.ts +105 -3
- package/dist/application.js +446 -18
- package/dist/artifacts/kinds.d.ts +35 -0
- package/dist/artifacts/kinds.js +177 -0
- package/dist/artifacts/publisher.d.ts +7 -0
- package/dist/artifacts/publisher.js +29 -0
- package/dist/artifacts/store.d.ts +119 -0
- package/dist/artifacts/store.js +581 -0
- package/dist/background/janitor.d.ts +22 -0
- package/dist/background/janitor.js +62 -0
- package/dist/background/mirror.d.ts +14 -0
- package/dist/background/mirror.js +41 -0
- package/dist/background/notify.d.ts +58 -0
- package/dist/background/notify.js +172 -0
- package/dist/background/output.d.ts +46 -0
- package/dist/background/output.js +179 -0
- package/dist/background/service.d.ts +137 -0
- package/dist/background/service.js +410 -0
- package/dist/background/store.d.ts +103 -0
- package/dist/background/store.js +195 -0
- package/dist/changelog/data.d.ts +6 -0
- package/dist/changelog/data.js +293 -0
- package/dist/changelog/index.d.ts +5 -0
- package/dist/changelog/index.js +7 -0
- package/dist/changelog/parse.d.ts +53 -0
- package/dist/changelog/parse.js +160 -0
- package/dist/commands/builtins.d.ts +10 -0
- package/dist/commands/builtins.js +161 -0
- package/dist/commands/catalog.d.ts +86 -0
- package/dist/commands/catalog.js +304 -0
- package/dist/config.d.ts +206 -6
- package/dist/config.js +426 -20
- package/dist/core/compaction.js +13 -2
- package/dist/core/contracts.d.ts +180 -3
- package/dist/core/opt-in.d.ts +12 -0
- package/dist/core/opt-in.js +12 -0
- package/dist/core/output-limit.d.ts +34 -0
- package/dist/core/output-limit.js +39 -0
- package/dist/core/run-stats.d.ts +16 -0
- package/dist/core/run-stats.js +8 -0
- package/dist/core/runner.d.ts +134 -4
- package/dist/core/runner.js +780 -138
- package/dist/core/timeout.d.ts +15 -0
- package/dist/core/timeout.js +61 -0
- package/dist/core/truncation.d.ts +30 -0
- package/dist/core/truncation.js +83 -0
- package/dist/goal/blockers.d.ts +45 -0
- package/dist/goal/blockers.js +51 -0
- package/dist/goal/budget.d.ts +24 -0
- package/dist/goal/budget.js +53 -0
- package/dist/goal/command.d.ts +36 -0
- package/dist/goal/command.js +98 -0
- package/dist/goal/format.d.ts +19 -0
- package/dist/goal/format.js +102 -0
- package/dist/goal/host.d.ts +29 -0
- package/dist/goal/host.js +42 -0
- package/dist/goal/index.d.ts +9 -0
- package/dist/goal/index.js +9 -0
- package/dist/goal/machine.d.ts +186 -0
- package/dist/goal/machine.js +344 -0
- package/dist/goal/prompts.d.ts +10 -0
- package/dist/goal/prompts.js +75 -0
- package/dist/goal/service.d.ts +113 -0
- package/dist/goal/service.js +273 -0
- package/dist/goal/store.d.ts +59 -0
- package/dist/goal/store.js +184 -0
- package/dist/index.d.ts +50 -5
- package/dist/index.js +49 -4
- package/dist/mcp/connector.js +38 -6
- package/dist/mcp/rich.d.ts +41 -0
- package/dist/mcp/rich.js +362 -0
- package/dist/permissions/modes.d.ts +72 -0
- package/dist/permissions/modes.js +98 -0
- package/dist/plan/exit-plan.d.ts +25 -0
- package/dist/plan/exit-plan.js +130 -0
- package/dist/plan/state.d.ts +102 -0
- package/dist/plan/state.js +165 -0
- package/dist/plugins/host.d.ts +34 -1
- package/dist/plugins/host.js +143 -9
- package/dist/providers/settings.d.ts +15 -0
- package/dist/providers/settings.js +38 -0
- package/dist/reload.d.ts +75 -0
- package/dist/reload.js +204 -0
- package/dist/resources/context.js +6 -1
- package/dist/runtime/access.d.ts +62 -0
- package/dist/runtime/access.js +115 -0
- package/dist/runtime/blobs.d.ts +51 -0
- package/dist/runtime/blobs.js +177 -0
- package/dist/runtime/diff.d.ts +23 -0
- package/dist/runtime/diff.js +154 -0
- package/dist/runtime/ids.d.ts +8 -0
- package/dist/runtime/ids.js +32 -0
- package/dist/runtime/paths.d.ts +11 -0
- package/dist/runtime/paths.js +17 -1
- package/dist/runtime/process.d.ts +22 -0
- package/dist/runtime/process.js +100 -15
- package/dist/runtime/sqlite.d.ts +2 -0
- package/dist/runtime/sqlite.js +18 -0
- package/dist/runtime/store.d.ts +91 -3
- package/dist/runtime/store.js +465 -11
- package/dist/runtime/zip.d.ts +24 -0
- package/dist/runtime/zip.js +126 -0
- package/dist/sessions/children.js +17 -1
- package/dist/sessions/side-questions.d.ts +57 -0
- package/dist/sessions/side-questions.js +161 -0
- package/dist/tools/analysis.d.ts +37 -0
- package/dist/tools/analysis.js +573 -0
- package/dist/tools/artifacts.d.ts +14 -0
- package/dist/tools/artifacts.js +143 -0
- package/dist/tools/background.d.ts +7 -0
- package/dist/tools/background.js +165 -0
- package/dist/tools/data.d.ts +7 -0
- package/dist/tools/data.js +160 -0
- package/dist/tools/execute.d.ts +5 -0
- package/dist/tools/execute.js +15 -3
- package/dist/tools/goal.d.ts +19 -0
- package/dist/tools/goal.js +126 -0
- package/dist/tools/search.d.ts +17 -3
- package/dist/tools/search.js +153 -0
- package/dist/tools/standard.d.ts +4 -1
- package/dist/tools/standard.js +99 -18
- package/dist/version.d.ts +5 -3
- package/dist/version.js +35 -6
- package/package.json +2 -2
package/dist/application.js
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { AgentDefinitionService } from "./agents/service.js";
|
|
5
|
+
import { CapabilityGrants } from "./analysis/capabilities.js";
|
|
6
|
+
import { DatasetService } from "./analysis/data/datasets.js";
|
|
7
|
+
import { AnalysisJanitor } from "./analysis/janitor.js";
|
|
8
|
+
import { AnalysisJobs } from "./analysis/jobs.js";
|
|
9
|
+
import { OciRuntime } from "./analysis/oci.js";
|
|
10
|
+
import { AnalysisRerun } from "./analysis/rerun.js";
|
|
11
|
+
import { AnalysisRuntimeManager } from "./analysis/runtime-manager.js";
|
|
12
|
+
import { createArtifactPublisher } from "./artifacts/publisher.js";
|
|
13
|
+
import { ArtifactStore } from "./artifacts/store.js";
|
|
14
|
+
import { TaskJanitor } from "./background/janitor.js";
|
|
15
|
+
import { subagentTasks } from "./background/mirror.js";
|
|
16
|
+
import { BackgroundTasks } from "./background/service.js";
|
|
3
17
|
import { configFile, configHome, loadConfigWithProvenance, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, stateHome, } from "./config.js";
|
|
4
18
|
import { completeText } from "./core/compaction.js";
|
|
19
|
+
import { DEFAULT_MAX_OUTPUT_TOKENS } from "./core/output-limit.js";
|
|
5
20
|
import { ToolRegistry } from "./core/registry.js";
|
|
6
21
|
import { AgentRunner } from "./core/runner.js";
|
|
22
|
+
import { GoalService } from "./goal/service.js";
|
|
23
|
+
import { GoalStore } from "./goal/store.js";
|
|
7
24
|
import { HerdrBridge } from "./integrations/herdr.js";
|
|
8
25
|
import { McpConnector } from "./mcp/connector.js";
|
|
9
26
|
import { discoverPlugins, PluginHost } from "./plugins/host.js";
|
|
@@ -13,10 +30,21 @@ import { ProviderSettingsStore } from "./providers/settings.js";
|
|
|
13
30
|
import { ProjectContext } from "./resources/context.js";
|
|
14
31
|
import { expandSlashPrompt, loadPromptTemplates, promptSources } from "./resources/prompts.js";
|
|
15
32
|
import { Skills, skillRoots } from "./resources/skills.js";
|
|
33
|
+
import { PathAccess } from "./runtime/access.js";
|
|
34
|
+
import { BlobStore } from "./runtime/blobs.js";
|
|
16
35
|
import { isPathSpec } from "./runtime/modules.js";
|
|
17
36
|
import { findWorkspace } from "./runtime/paths.js";
|
|
18
37
|
import { SQLiteStore } from "./runtime/store.js";
|
|
19
38
|
import { ChildSessions } from "./sessions/children.js";
|
|
39
|
+
import { SideQuestions } from "./sessions/side-questions.js";
|
|
40
|
+
import { registerGoalTools } from "./tools/goal.js";
|
|
41
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
|
+
/**
|
|
43
|
+
* Cap per teardown stage of `app.close()`: exit must never wait longer than this on a hanging
|
|
44
|
+
* component (MCP server, provider, plugin). Stages run in parallel where independent and
|
|
45
|
+
* failures are swallowed, so teardown errors never replace the caller's result.
|
|
46
|
+
*/
|
|
47
|
+
const TEARDOWN_STAGE_TIMEOUT_MS = 2_500;
|
|
20
48
|
/** A built-in runs unless its config sets `enabled: false` or it is disabled by id. */
|
|
21
49
|
export function enabledBuiltins(available, config, disabled = []) {
|
|
22
50
|
for (const id of [...Object.keys(config), ...disabled])
|
|
@@ -52,7 +80,28 @@ export async function createApplication(options = {}) {
|
|
|
52
80
|
// a project value (see loadConfigWithProvenance), so a project cannot grant itself MCP consent.
|
|
53
81
|
const mcpAllow = config.mcp.allow === true;
|
|
54
82
|
let persistedMcpAllow = mcpAllow;
|
|
83
|
+
// Extra tool roots: config `additionalDirectories` (already canonicalized on load) plus per-run
|
|
84
|
+
// `--add-dir` (resolved against the current directory). Ignored entirely under --read-only, so
|
|
85
|
+
// the flag and the config key can never widen a locked session.
|
|
86
|
+
const addDirs = options.readOnly
|
|
87
|
+
? []
|
|
88
|
+
: await Promise.all((options.addDirs ?? []).map((dir) => realpath(resolve(cwd, dir)).catch(() => resolve(cwd, dir))));
|
|
89
|
+
const pathAccess = new PathAccess({
|
|
90
|
+
workspace,
|
|
91
|
+
extraRoots: [...config.additionalDirectories, ...addDirs],
|
|
92
|
+
...(options.approveExternalDirectory && !options.readOnly
|
|
93
|
+
? { approve: options.approveExternalDirectory }
|
|
94
|
+
: {}),
|
|
95
|
+
readOnly: !!options.readOnly,
|
|
96
|
+
});
|
|
55
97
|
const store = new SQLiteStore(options.db ?? join(stateHome(), "sessions.sqlite"));
|
|
98
|
+
// Content-addressed attachment bytes next to the session database (no I/O until `put`).
|
|
99
|
+
const blobs = new BlobStore({
|
|
100
|
+
root: options.db && options.db !== ":memory:"
|
|
101
|
+
? join(dirname(options.db), "blobs")
|
|
102
|
+
: join(stateHome(), "blobs"),
|
|
103
|
+
db: store.db,
|
|
104
|
+
});
|
|
56
105
|
const registry = new ToolRegistry(), skills = new Skills({ overrides: config.skillOverrides }), context = new ProjectContext(workspace, {
|
|
57
106
|
globalDir: configHome(),
|
|
58
107
|
cwd,
|
|
@@ -234,7 +283,150 @@ export async function createApplication(options = {}) {
|
|
|
234
283
|
}, {
|
|
235
284
|
config: config.websearch,
|
|
236
285
|
resolveExtension: () => plugins.extensions.resolve("websearch"),
|
|
286
|
+
}, pathAccess);
|
|
287
|
+
// Python analysis and artifacts live next to the session database (like attachment blobs).
|
|
288
|
+
const stateRoot = options.db && options.db !== ":memory:" ? dirname(options.db) : stateHome();
|
|
289
|
+
const artifacts = new ArtifactStore({
|
|
290
|
+
root: stateRoot,
|
|
291
|
+
db: store.db,
|
|
292
|
+
limits: {
|
|
293
|
+
maxFiles: config.analysis.limits.maxFiles,
|
|
294
|
+
maxFileBytes: config.analysis.limits.maxFileBytes,
|
|
295
|
+
maxOutputBytes: config.analysis.limits.maxOutputBytes,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
const capabilityGrants = new CapabilityGrants({
|
|
299
|
+
db: store.db,
|
|
300
|
+
rootOf: (id) => store.rootOf(id),
|
|
301
|
+
});
|
|
302
|
+
const analysisRuntime = options.analysisRuntime ??
|
|
303
|
+
new AnalysisRuntimeManager({
|
|
304
|
+
stateDir: stateRoot,
|
|
305
|
+
...(options.python ? { python: options.python } : {}),
|
|
306
|
+
});
|
|
307
|
+
const analysisJobs = new AnalysisJobs({ root: stateRoot, db: store.db });
|
|
308
|
+
// Container mode (`analysis.runtime: "oci"`): read from the live config on every call.
|
|
309
|
+
let ociRuntime;
|
|
310
|
+
const oci = {
|
|
311
|
+
mode: () => config.analysis.runtime,
|
|
312
|
+
runtime: () => {
|
|
313
|
+
const key = JSON.stringify(config.analysis.oci);
|
|
314
|
+
if (ociRuntime?.key !== key)
|
|
315
|
+
ociRuntime = {
|
|
316
|
+
key,
|
|
317
|
+
runtime: new OciRuntime({ ...config.analysis.oci }, options.analysisOci),
|
|
318
|
+
};
|
|
319
|
+
return ociRuntime.runtime;
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
// Tabular datasets (one SQLite file each); XLSX needs the discovered Python.
|
|
323
|
+
const datasets = new DatasetService({
|
|
324
|
+
root: stateRoot,
|
|
325
|
+
db: store.db,
|
|
326
|
+
limits: config.analysis.data,
|
|
327
|
+
python: analysisRuntime,
|
|
237
328
|
});
|
|
329
|
+
const analysisRerun = new AnalysisRerun({ jobs: analysisJobs, store: artifacts, datasets });
|
|
330
|
+
// Retention: a sweep at most every 24 h, launched in the background after the start.
|
|
331
|
+
const janitor = new AnalysisJanitor({
|
|
332
|
+
root: stateRoot,
|
|
333
|
+
db: store.db,
|
|
334
|
+
artifacts,
|
|
335
|
+
datasets,
|
|
336
|
+
blobs,
|
|
337
|
+
retention: () => config.analysis.retention,
|
|
338
|
+
...(options.analysisSweepDelayMs !== undefined
|
|
339
|
+
? { initialDelayMs: options.analysisSweepDelayMs }
|
|
340
|
+
: {}),
|
|
341
|
+
});
|
|
342
|
+
// Background tasks: a service over the shared database (state change = compare-and-set), logs
|
|
343
|
+
// under `<state>/tasks/`. Tasks of a dead owner process (a previous run) become `lost` now.
|
|
344
|
+
const tasks = new BackgroundTasks({
|
|
345
|
+
db: store.db,
|
|
346
|
+
stateRoot,
|
|
347
|
+
rootOf: (id) => store.rootOf(id),
|
|
348
|
+
limits: () => config.tasks,
|
|
349
|
+
readOnly: !!options.readOnly,
|
|
350
|
+
...(options.tasks?.wake ? { wake: options.tasks.wake } : {}),
|
|
351
|
+
...(options.tasks?.onChange ? { onChange: options.tasks.onChange } : {}),
|
|
352
|
+
...(options.tasks?.timing ? { timing: options.tasks.timing } : {}),
|
|
353
|
+
...(options.tasks?.maxPerProcess ? { maxPerProcess: options.tasks.maxPerProcess } : {}),
|
|
354
|
+
...(options.tasks?.killGraceMs ? { killGraceMs: options.tasks.killGraceMs } : {}),
|
|
355
|
+
mirror: (root) => subagentTasks(plugins.panels, root),
|
|
356
|
+
onError: (error) => process.stderr.write(`Background task error: ${error instanceof Error ? error.message : String(error)}\n`),
|
|
357
|
+
});
|
|
358
|
+
tasks.recover();
|
|
359
|
+
const taskJanitor = new TaskJanitor({
|
|
360
|
+
store: tasks.store,
|
|
361
|
+
stateRoot,
|
|
362
|
+
retentionDays: () => config.tasks.retentionDays,
|
|
363
|
+
...(options.tasks?.sweepDelayMs !== undefined
|
|
364
|
+
? { initialDelayMs: options.tasks.sweepDelayMs }
|
|
365
|
+
: {}),
|
|
366
|
+
});
|
|
367
|
+
if (config.tasks.enabled) {
|
|
368
|
+
const { registerBackgroundTools } = await import("./tools/background.js");
|
|
369
|
+
registerBackgroundTools(registry, { tasks, rootOf: (id) => store.rootOf(id) });
|
|
370
|
+
}
|
|
371
|
+
// Session goals (`/goal`): one service over the shared database. A goal left `active` by a
|
|
372
|
+
// process that is gone pauses with reason `restart` now (never auto-resumed). The two goal
|
|
373
|
+
// tools are opt-in: a run only sees them while its session has an active goal.
|
|
374
|
+
const goals = new GoalService({
|
|
375
|
+
store: new GoalStore(store.db),
|
|
376
|
+
settings: () => config.goal,
|
|
377
|
+
...(options.goals?.onChange ? { onChange: options.goals.onChange } : {}),
|
|
378
|
+
...(options.goals?.onLog ? { onLog: options.goals.onLog } : {}),
|
|
379
|
+
});
|
|
380
|
+
if (options.goals?.recover !== false)
|
|
381
|
+
goals.recover();
|
|
382
|
+
registerGoalTools(registry, { goals });
|
|
383
|
+
// R4: nothing is registered with analysis disabled or under --read-only. Startup only looks
|
|
384
|
+
// for candidate interpreters with `stat`; no process is launched until the first call.
|
|
385
|
+
const analysisEnabled = config.analysis.enabled && !options.readOnly;
|
|
386
|
+
if (analysisEnabled) {
|
|
387
|
+
const [candidates, active] = await Promise.all([
|
|
388
|
+
analysisRuntime.candidates(),
|
|
389
|
+
analysisRuntime.activeRuntime(),
|
|
390
|
+
]);
|
|
391
|
+
const { registerAnalysisTools } = await import("./tools/analysis.js");
|
|
392
|
+
registerAnalysisTools(registry, {
|
|
393
|
+
store: artifacts,
|
|
394
|
+
jobs: analysisJobs,
|
|
395
|
+
runtime: analysisRuntime,
|
|
396
|
+
datasets,
|
|
397
|
+
oci,
|
|
398
|
+
rerun: analysisRerun,
|
|
399
|
+
rootOf: (id) => store.rootOf(id),
|
|
400
|
+
// Read live: a changed `analysis.limits.*` setting applies to the next call.
|
|
401
|
+
limits: {
|
|
402
|
+
get timeoutMs() {
|
|
403
|
+
return config.analysis.limits.timeoutMs;
|
|
404
|
+
},
|
|
405
|
+
get maxLogBytes() {
|
|
406
|
+
return config.analysis.limits.maxLogBytes;
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
startup: { candidates: candidates.length > 0, extras: active?.extras ?? [] },
|
|
410
|
+
});
|
|
411
|
+
const { registerArtifactTools } = await import("./tools/artifacts.js");
|
|
412
|
+
registerArtifactTools(registry, { store: artifacts, rootOf: (id) => store.rootOf(id) });
|
|
413
|
+
}
|
|
414
|
+
// `exit_plan` (opt-in: only the plan agent's run sees it): the plan review hand-over.
|
|
415
|
+
const { registerExitPlan } = await import("./plan/exit-plan.js");
|
|
416
|
+
registerExitPlan(registry, { store, ui: plugins.ui, readOnly: !!options.readOnly });
|
|
417
|
+
// Data tools only read (effect `read`): they stay available under --read-only.
|
|
418
|
+
if (config.analysis.enabled) {
|
|
419
|
+
const { registerDataTools } = await import("./tools/data.js");
|
|
420
|
+
registerDataTools(registry, { datasets, rootOf: (id) => store.rootOf(id) });
|
|
421
|
+
}
|
|
422
|
+
const sessionWorkspace = (sessionId) => {
|
|
423
|
+
try {
|
|
424
|
+
return store.get(sessionId).workspace;
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
return workspace;
|
|
428
|
+
}
|
|
429
|
+
};
|
|
238
430
|
const { registerPluginInstallTool } = await import("./plugins/install.js");
|
|
239
431
|
registerPluginInstallTool(registry, { readOnly: !!options.readOnly });
|
|
240
432
|
if ((options.allowMcp || mcpAllow) && !options.readOnly)
|
|
@@ -475,6 +667,7 @@ export async function createApplication(options = {}) {
|
|
|
475
667
|
const runtimePolicy = {
|
|
476
668
|
write: !!options.allowWrite && !options.readOnly,
|
|
477
669
|
process: !!options.allowProcess && !options.readOnly,
|
|
670
|
+
...(options.allowAnalysis && !options.readOnly ? { analysis: true } : {}),
|
|
478
671
|
external: !options.readOnly &&
|
|
479
672
|
(!!options.allowExternal ||
|
|
480
673
|
!!options.allowMcp ||
|
|
@@ -483,6 +676,52 @@ export async function createApplication(options = {}) {
|
|
|
483
676
|
plugins.externalCount > 0 ||
|
|
484
677
|
registry.list().some((t) => t.name.startsWith("p_"))),
|
|
485
678
|
};
|
|
679
|
+
/** Model and provider of a session, for the provenance of its artifacts. */
|
|
680
|
+
const provenanceOf = (sessionId) => {
|
|
681
|
+
try {
|
|
682
|
+
const session = store.get(sessionId);
|
|
683
|
+
return {
|
|
684
|
+
...(session.model ? { model: session.model } : {}),
|
|
685
|
+
...(session.provider ? { provider: session.provider } : {}),
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
catch {
|
|
689
|
+
return {};
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
/**
|
|
693
|
+
* The maximum output a model's catalog declares (`ModelInfo.maxOutputTokens`), for the budget
|
|
694
|
+
* of each request. Looks in the active provider's catalog first (loading it once, bounded, if
|
|
695
|
+
* the first request comes before it); then in the catalogs of the configured profiles that
|
|
696
|
+
* were already listed, taking the smallest declaration when several providers list the same
|
|
697
|
+
* model id so a derived budget never exceeds any of them. Unknown stays unknown.
|
|
698
|
+
*/
|
|
699
|
+
const declaredOutputLimit = async (model) => {
|
|
700
|
+
if (!models.has(model)) {
|
|
701
|
+
const loading = ensureModels();
|
|
702
|
+
if (loading) {
|
|
703
|
+
let timer;
|
|
704
|
+
await Promise.race([
|
|
705
|
+
loading.catch(() => undefined),
|
|
706
|
+
new Promise((resolve) => {
|
|
707
|
+
timer = setTimeout(resolve, 2_000);
|
|
708
|
+
}),
|
|
709
|
+
]);
|
|
710
|
+
if (timer)
|
|
711
|
+
clearTimeout(timer);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const own = models.get(model)?.maxOutputTokens;
|
|
715
|
+
if (own !== undefined)
|
|
716
|
+
return own;
|
|
717
|
+
const declared = catalogCache
|
|
718
|
+
? availableProviderModels(catalogCache.value)
|
|
719
|
+
.filter((entry) => entry.model.id === model)
|
|
720
|
+
.map((entry) => entry.model.maxOutputTokens)
|
|
721
|
+
.filter((value) => typeof value === "number" && value > 0)
|
|
722
|
+
: [];
|
|
723
|
+
return declared.length ? Math.min(...declared) : undefined;
|
|
724
|
+
};
|
|
486
725
|
const runner = new AgentRunner({
|
|
487
726
|
provider,
|
|
488
727
|
providerFor,
|
|
@@ -495,7 +734,43 @@ export async function createApplication(options = {}) {
|
|
|
495
734
|
compaction: config.compaction,
|
|
496
735
|
extensions: plugins,
|
|
497
736
|
contextWindow,
|
|
737
|
+
modelOutputLimit: declaredOutputLimit,
|
|
738
|
+
fallbackMaxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS,
|
|
739
|
+
effortLevels: (model) => {
|
|
740
|
+
const known = models.get(model)?.effort;
|
|
741
|
+
if (!known)
|
|
742
|
+
void ensureModels();
|
|
743
|
+
return known;
|
|
744
|
+
},
|
|
498
745
|
...(options.approve && !options.readOnly ? { approve: options.approve } : {}),
|
|
746
|
+
pathAccess,
|
|
747
|
+
...(analysisEnabled
|
|
748
|
+
? {
|
|
749
|
+
capabilities: {
|
|
750
|
+
granted: (capability, sessionId) => capabilityGrants.granted(capability, sessionId),
|
|
751
|
+
record: (input) => {
|
|
752
|
+
capabilityGrants.record({ ...input, workspace: sessionWorkspace(input.sessionId) });
|
|
753
|
+
},
|
|
754
|
+
source: options.approvalSource ?? "tui",
|
|
755
|
+
get runtime() {
|
|
756
|
+
return config.analysis.runtime;
|
|
757
|
+
},
|
|
758
|
+
// A rerun's approval shows the saved script (the input carries only its reference).
|
|
759
|
+
preview: async (input, sessionId) => typeof input.rerunOf === "string"
|
|
760
|
+
? analysisRerun.script(input.rerunOf, store.rootOf(sessionId))
|
|
761
|
+
: undefined,
|
|
762
|
+
},
|
|
763
|
+
}
|
|
764
|
+
: {}),
|
|
765
|
+
artifacts: (call, announce) => createArtifactPublisher(artifacts, {
|
|
766
|
+
sessionId: call.sessionId,
|
|
767
|
+
rootSessionId: store.rootOf(call.sessionId),
|
|
768
|
+
workspace: sessionWorkspace(call.sessionId),
|
|
769
|
+
runId: call.runId,
|
|
770
|
+
callId: call.callId,
|
|
771
|
+
// Public provenance of what produced the artifact (no paths, no keys).
|
|
772
|
+
provenance: provenanceOf(call.sessionId),
|
|
773
|
+
}, announce),
|
|
499
774
|
...(config.websearch.provider === "native"
|
|
500
775
|
? { nativeTools: [{ type: config.websearch.nativeToolType }] }
|
|
501
776
|
: {}),
|
|
@@ -508,6 +783,10 @@ export async function createApplication(options = {}) {
|
|
|
508
783
|
// Child sessions (generic delegation service for plugins). Contexts are cached per workspace
|
|
509
784
|
// so a child in a git worktree reads that worktree's AGENTS.md files.
|
|
510
785
|
const contexts = new Map([[workspace, context]]);
|
|
786
|
+
// Startup reconciliation: runs left queued/running by a dead process become `interrupted`
|
|
787
|
+
// (runs of this process, e.g. another Application in a server, are kept). Child sessions are
|
|
788
|
+
// reconciled by `interruptStale()` in the ChildSessions constructor right below.
|
|
789
|
+
store.interruptRuns();
|
|
511
790
|
plugins.setSessions(new ChildSessions({
|
|
512
791
|
store,
|
|
513
792
|
runner,
|
|
@@ -574,12 +853,43 @@ export async function createApplication(options = {}) {
|
|
|
574
853
|
}
|
|
575
854
|
}
|
|
576
855
|
}
|
|
856
|
+
// Retention runs in the background (unreferenced timer); never for in-memory test databases.
|
|
857
|
+
if (analysisEnabled && options.db !== ":memory:")
|
|
858
|
+
janitor.start();
|
|
859
|
+
// Retention is maintenance that deletes files: never under --read-only, never for `:memory:`.
|
|
860
|
+
if (options.db !== ":memory:" && !options.readOnly)
|
|
861
|
+
taskJanitor.start();
|
|
577
862
|
return {
|
|
578
863
|
workspace,
|
|
864
|
+
pathAccess,
|
|
579
865
|
config,
|
|
580
866
|
prompts,
|
|
581
867
|
expandPrompt,
|
|
582
868
|
store,
|
|
869
|
+
/** Uploaded attachment bytes; resolve a `BlobRef` with `blobs.attachment(ref)` before a run. */
|
|
870
|
+
blobs,
|
|
871
|
+
/** Published artifacts of every session (folders under `<state>/artifacts`). */
|
|
872
|
+
artifacts,
|
|
873
|
+
/** Persisted capability grants (`analysis.run`) and their audit rows. */
|
|
874
|
+
capabilityGrants,
|
|
875
|
+
/** Background tasks (`bg_*` tools, `/tasks` panels): admission, state, output, stop. */
|
|
876
|
+
tasks,
|
|
877
|
+
/** Session goals (`/goal`): state, user actions, the continuation controller. */
|
|
878
|
+
goals,
|
|
879
|
+
/** Python discovery and job folders of `python_run`; `enabled` is false under --read-only. */
|
|
880
|
+
analysis: {
|
|
881
|
+
enabled: analysisEnabled,
|
|
882
|
+
runtime: analysisRuntime,
|
|
883
|
+
jobs: analysisJobs,
|
|
884
|
+
/** Container runtime of the current configuration (status, pull); mode `managed` ignores it. */
|
|
885
|
+
oci,
|
|
886
|
+
rerun: analysisRerun,
|
|
887
|
+
janitor,
|
|
888
|
+
},
|
|
889
|
+
/** Keys a project layer set that only the user layer decides (ignored on load). */
|
|
890
|
+
configDiagnostics: configProvenance.ignored ?? [],
|
|
891
|
+
/** Tabular datasets of every session (ingestion, read-only queries, pages). */
|
|
892
|
+
datasets,
|
|
583
893
|
registry,
|
|
584
894
|
context,
|
|
585
895
|
skills,
|
|
@@ -672,12 +982,15 @@ export async function createApplication(options = {}) {
|
|
|
672
982
|
mcpStartupFailures() {
|
|
673
983
|
return [...mcpStartupFailures];
|
|
674
984
|
},
|
|
675
|
-
/**
|
|
985
|
+
/**
|
|
986
|
+
* Host-owned interactive consent boundary: the TUI and the web UI (`alisio serve`, after an
|
|
987
|
+
* explicit confirmation dialog) may grant. Headless callers remain blocked unless flagged.
|
|
988
|
+
*/
|
|
676
989
|
grantMcpRuntimePermission(request) {
|
|
677
990
|
if (!request.confirmed)
|
|
678
991
|
return mcpRuntimePermission;
|
|
679
|
-
if (request.source !== "interactive-tui")
|
|
680
|
-
throw new Error("MCP runtime permission may only be granted by
|
|
992
|
+
if (request.source !== "interactive-tui" && request.source !== "interactive-web")
|
|
993
|
+
throw new Error("MCP runtime permission may only be granted by an interactive UI");
|
|
681
994
|
if (options.readOnly)
|
|
682
995
|
throw new Error("MCP is unavailable under --read-only");
|
|
683
996
|
if (mcpRuntimePermission === "granted")
|
|
@@ -688,12 +1001,12 @@ export async function createApplication(options = {}) {
|
|
|
688
1001
|
return mcpRuntimePermission;
|
|
689
1002
|
},
|
|
690
1003
|
/** Persist global consent atomically, then grant runtime permission for this process. */
|
|
691
|
-
async rememberGlobalMcpConsent() {
|
|
1004
|
+
async rememberGlobalMcpConsent(source = "interactive-tui") {
|
|
692
1005
|
if (options.readOnly)
|
|
693
1006
|
throw new Error("MCP is unavailable under --read-only");
|
|
694
1007
|
await setGlobalMcpAllow({ allow: true });
|
|
695
1008
|
persistedMcpAllow = true;
|
|
696
|
-
this.grantMcpRuntimePermission({ source
|
|
1009
|
+
this.grantMcpRuntimePermission({ source, confirmed: true });
|
|
697
1010
|
},
|
|
698
1011
|
/**
|
|
699
1012
|
* Clear the persisted global consent atomically and drop the runtime permission (disconnecting
|
|
@@ -736,6 +1049,8 @@ export async function createApplication(options = {}) {
|
|
|
736
1049
|
* (next instructions load), `websearch.provider` mutates the shared config object the search
|
|
737
1050
|
* chain reads per call, `pluginHooks.timeoutMs` lands in the plugin host (next hook call),
|
|
738
1051
|
* and `tui.*` is persisted for the host to apply. Nothing in this set requires a restart.
|
|
1052
|
+
* `agents.*` (active agent id and reasoning effort) is persisted for the TUI to consume:
|
|
1053
|
+
* the next `/agents`/`/effort` selection and the next prompt already read the live config.
|
|
739
1054
|
* `mcp.allow` is deliberately not routed here: it flows through
|
|
740
1055
|
* `rememberGlobalMcpConsent`/`revokeGlobalMcpConsent` instead.
|
|
741
1056
|
* Throws under `--read-only`. Returns the written config file.
|
|
@@ -767,6 +1082,9 @@ export async function createApplication(options = {}) {
|
|
|
767
1082
|
case "limits.maxTurns":
|
|
768
1083
|
case "limits.maxOutputTokens":
|
|
769
1084
|
case "limits.maxContextChars":
|
|
1085
|
+
case "limits.firstTokenTimeoutMs":
|
|
1086
|
+
case "limits.firstTokenRetries":
|
|
1087
|
+
case "limits.truncationRecoveries":
|
|
770
1088
|
case "limits.timeoutMs": {
|
|
771
1089
|
const patch = limitLeaf(key.slice("limits.".length));
|
|
772
1090
|
config.limits = { ...config.limits, ...patch };
|
|
@@ -780,9 +1098,74 @@ export async function createApplication(options = {}) {
|
|
|
780
1098
|
case "tui.paddingX":
|
|
781
1099
|
config.tui = { ...config.tui, paddingX: Number(value) };
|
|
782
1100
|
break;
|
|
1101
|
+
case "tui.contentPaddingX":
|
|
1102
|
+
config.tui = { ...config.tui, contentPaddingX: Number(value) };
|
|
1103
|
+
break;
|
|
783
1104
|
case "tui.skillSlashCommands":
|
|
784
1105
|
config.tui = { ...config.tui, skillSlashCommands: value === true };
|
|
785
1106
|
break;
|
|
1107
|
+
case "agents.active":
|
|
1108
|
+
config.agents = { ...config.agents, active: String(value) };
|
|
1109
|
+
break;
|
|
1110
|
+
case "agents.effort":
|
|
1111
|
+
// Clearing (undefined) drops the leaf from the live config and the JSON file.
|
|
1112
|
+
config.agents =
|
|
1113
|
+
value === undefined
|
|
1114
|
+
? { active: config.agents.active }
|
|
1115
|
+
: { ...config.agents, effort: String(value) };
|
|
1116
|
+
break;
|
|
1117
|
+
case "analysis.enabled":
|
|
1118
|
+
// Tools are registered when the application starts: the change applies to the next
|
|
1119
|
+
// application (a restart; the web recycles the workspace application).
|
|
1120
|
+
config.analysis = { ...config.analysis, enabled: value === true };
|
|
1121
|
+
break;
|
|
1122
|
+
case "analysis.limits.timeoutMs":
|
|
1123
|
+
// Read live by python_run on its next call.
|
|
1124
|
+
config.analysis = {
|
|
1125
|
+
...config.analysis,
|
|
1126
|
+
limits: { ...config.analysis.limits, timeoutMs: Number(value) },
|
|
1127
|
+
};
|
|
1128
|
+
break;
|
|
1129
|
+
case "analysis.retention.jobsDays":
|
|
1130
|
+
case "analysis.retention.intermediateDays":
|
|
1131
|
+
case "analysis.retention.artifactsDays": {
|
|
1132
|
+
// Read by the next sweep.
|
|
1133
|
+
const leaf = key.slice("analysis.retention.".length);
|
|
1134
|
+
config.analysis = {
|
|
1135
|
+
...config.analysis,
|
|
1136
|
+
retention: { ...config.analysis.retention, [leaf]: Number(value) },
|
|
1137
|
+
};
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1140
|
+
case "goal.enabled":
|
|
1141
|
+
case "goal.maxTurns":
|
|
1142
|
+
case "goal.maxMinutes":
|
|
1143
|
+
case "goal.repeatedReplyLimit":
|
|
1144
|
+
case "goal.noToolTurnsLimit":
|
|
1145
|
+
case "goal.blockedRepeats": {
|
|
1146
|
+
// Read live: the next continuation (or the next `/goal`) uses the new value. The caps
|
|
1147
|
+
// of a goal already running were snapshotted when it was created.
|
|
1148
|
+
const leaf = key.slice("goal.".length);
|
|
1149
|
+
config.goal = {
|
|
1150
|
+
...config.goal,
|
|
1151
|
+
[leaf]: leaf === "enabled" ? value === true : Number(value),
|
|
1152
|
+
};
|
|
1153
|
+
break;
|
|
1154
|
+
}
|
|
1155
|
+
case "tasks.enabled":
|
|
1156
|
+
// Tools are registered when the application starts: the change applies to the next
|
|
1157
|
+
// application (a restart; the web recycles the workspace application).
|
|
1158
|
+
config.tasks = { ...config.tasks, enabled: value === true };
|
|
1159
|
+
break;
|
|
1160
|
+
case "tasks.maxPerSession":
|
|
1161
|
+
case "tasks.maxRunMs":
|
|
1162
|
+
case "tasks.maxOutputBytes":
|
|
1163
|
+
case "tasks.retentionDays": {
|
|
1164
|
+
// Read live: the next task (or the next sweep) uses the new value.
|
|
1165
|
+
const leaf = key.slice("tasks.".length);
|
|
1166
|
+
config.tasks = { ...config.tasks, [leaf]: Number(value) };
|
|
1167
|
+
break;
|
|
1168
|
+
}
|
|
786
1169
|
case "websearch.provider":
|
|
787
1170
|
// Mutated in place (same object identity) so the tool chain's per-call
|
|
788
1171
|
// `websearchCtx.config` sees the new provider on the very next search call.
|
|
@@ -802,6 +1185,40 @@ export async function createApplication(options = {}) {
|
|
|
802
1185
|
return providerInfo;
|
|
803
1186
|
},
|
|
804
1187
|
runner,
|
|
1188
|
+
/** `/btw` side questions: tool-less answers about a session outside its conversation. */
|
|
1189
|
+
sideQuestions: new SideQuestions({ runner, state: store }),
|
|
1190
|
+
/**
|
|
1191
|
+
* User agent files (`<workspace>/.agents/agents`, `~/.agents/agents`). Every write asks the
|
|
1192
|
+
* subagents plugin (the runtime agent registry) to rediscover through its `/agents reload`
|
|
1193
|
+
* verb and reports whether the saved file is loaded; without the plugin nothing reloads.
|
|
1194
|
+
*/
|
|
1195
|
+
agentDefinitions: new AgentDefinitionService({
|
|
1196
|
+
workspace,
|
|
1197
|
+
home: homedir(),
|
|
1198
|
+
registry: {
|
|
1199
|
+
async reload() {
|
|
1200
|
+
const handler = plugins.commands.get("agents");
|
|
1201
|
+
if (!handler)
|
|
1202
|
+
return false;
|
|
1203
|
+
await handler("reload");
|
|
1204
|
+
return Array.isArray(plugins.pluginState("subagents", "definitions"));
|
|
1205
|
+
},
|
|
1206
|
+
loadedPaths() {
|
|
1207
|
+
let state;
|
|
1208
|
+
try {
|
|
1209
|
+
state = plugins.pluginState("subagents", "definitions");
|
|
1210
|
+
}
|
|
1211
|
+
catch {
|
|
1212
|
+
return undefined;
|
|
1213
|
+
}
|
|
1214
|
+
if (!Array.isArray(state))
|
|
1215
|
+
return undefined;
|
|
1216
|
+
return state
|
|
1217
|
+
.map((entry) => entry?.path)
|
|
1218
|
+
.filter((path) => typeof path === "string");
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1221
|
+
}),
|
|
805
1222
|
herdr,
|
|
806
1223
|
contextWindow,
|
|
807
1224
|
/** Effective context budget + compaction point shared by the runner and the TUI bar. */
|
|
@@ -900,24 +1317,34 @@ export async function createApplication(options = {}) {
|
|
|
900
1317
|
});
|
|
901
1318
|
},
|
|
902
1319
|
async close() {
|
|
1320
|
+
// Exit latency is user-facing: tear down herdr + MCP in parallel, then provider +
|
|
1321
|
+
// plugins (store last). Every stage is raced against a cap so a single hanging
|
|
1322
|
+
// component cannot stall /exit; teardown errors are swallowed for callers.
|
|
1323
|
+
const capped = (work) => Promise.race([work.catch(() => { }), delay(TEARDOWN_STAGE_TIMEOUT_MS)]);
|
|
903
1324
|
try {
|
|
904
|
-
|
|
905
|
-
|
|
1325
|
+
janitor.stop();
|
|
1326
|
+
taskJanitor.stop();
|
|
1327
|
+
datasets?.close();
|
|
1328
|
+
// Tasks first in the same stage: they end with Alisio (process groups killed).
|
|
1329
|
+
await capped(Promise.allSettled([herdr.close(), mcp.close(), tasks.close()]));
|
|
906
1330
|
}
|
|
907
1331
|
finally {
|
|
908
1332
|
try {
|
|
909
|
-
await
|
|
910
|
-
|
|
911
|
-
...new Set([
|
|
1333
|
+
await capped((async () => {
|
|
1334
|
+
const runtimes = new Set([
|
|
912
1335
|
...(await Promise.allSettled(targetProviders.values()))
|
|
913
1336
|
.filter((x) => x.status === "fulfilled")
|
|
914
1337
|
.map((x) => x.value),
|
|
915
1338
|
...retainedProviders,
|
|
916
|
-
])
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1339
|
+
]);
|
|
1340
|
+
await Promise.allSettled([
|
|
1341
|
+
provider.dispose(),
|
|
1342
|
+
...[...runtimes]
|
|
1343
|
+
.filter((runtime) => runtime !== provider.currentProvider)
|
|
1344
|
+
.map((runtime) => Promise.resolve(runtime.dispose?.())),
|
|
1345
|
+
plugins.close(),
|
|
1346
|
+
]);
|
|
1347
|
+
})());
|
|
921
1348
|
}
|
|
922
1349
|
finally {
|
|
923
1350
|
store.close();
|
|
@@ -927,9 +1354,10 @@ export async function createApplication(options = {}) {
|
|
|
927
1354
|
};
|
|
928
1355
|
}
|
|
929
1356
|
catch (e) {
|
|
930
|
-
await
|
|
931
|
-
|
|
932
|
-
|
|
1357
|
+
await Promise.race([
|
|
1358
|
+
Promise.allSettled([herdr.close(), mcp.close(), plugins.close()]),
|
|
1359
|
+
delay(TEARDOWN_STAGE_TIMEOUT_MS),
|
|
1360
|
+
]);
|
|
933
1361
|
store.close();
|
|
934
1362
|
throw e;
|
|
935
1363
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact type registry: the kind, the served MIME type and whether a rich client may preview a
|
|
3
|
+
* published file. The type comes from the extension AND the leading bytes; when they disagree the
|
|
4
|
+
* most restrictive type wins (`file`, download only). The served `Content-Type` always comes from
|
|
5
|
+
* here, never from the script that wrote the file.
|
|
6
|
+
*/
|
|
7
|
+
import type { ArtifactKind } from "@alisio/sdk";
|
|
8
|
+
export interface ArtifactType {
|
|
9
|
+
kind: ArtifactKind;
|
|
10
|
+
mimeType: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const DOWNLOAD_ONLY: ArtifactType;
|
|
13
|
+
/** Lower-case extension without the dot (`""` when none). */
|
|
14
|
+
export declare const extensionOf: (fileName: string) => string;
|
|
15
|
+
/** Text: no NUL byte in the sampled head and the sample decodes as UTF-8. */
|
|
16
|
+
export declare function looksLikeText(head: Uint8Array): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The type of a file from its name and its first bytes (at least 4 KiB is a good sample). An
|
|
19
|
+
* unknown extension, or bytes that contradict the extension, yield `file` (download only).
|
|
20
|
+
* An empty file keeps the type of its extension.
|
|
21
|
+
*/
|
|
22
|
+
export declare function classifyArtifact(fileName: string, head: Uint8Array): ArtifactType;
|
|
23
|
+
/** Whether a rich client may preview a file of this name, type and size. */
|
|
24
|
+
export declare function isPreviewable(fileName: string, type: ArtifactType, bytes: number): boolean;
|
|
25
|
+
/** English labels of each kind (the web localizes its own; text surfaces use these). */
|
|
26
|
+
export declare const KIND_LABELS: Record<ArtifactKind, string>;
|
|
27
|
+
/** The type the sandboxed viewer serves for one file (`application/octet-stream` when unknown). */
|
|
28
|
+
export declare const viewerContentType: (fileName: string) => string;
|
|
29
|
+
/**
|
|
30
|
+
* The registry type of one file inside an artifact (for the authenticated `files/*` route):
|
|
31
|
+
* the extension and the leading bytes decide, exactly like a published file.
|
|
32
|
+
*/
|
|
33
|
+
export declare function fileType(fileName: string, head: Uint8Array): ArtifactType & {
|
|
34
|
+
inline: boolean;
|
|
35
|
+
};
|