@alisio/core 0.1.0-alpha.21 → 0.1.0-alpha.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/analysis/capabilities.d.ts +48 -0
  2. package/dist/analysis/capabilities.js +90 -0
  3. package/dist/analysis/data/csv.d.ts +46 -0
  4. package/dist/analysis/data/csv.js +240 -0
  5. package/dist/analysis/data/datasets.d.ts +132 -0
  6. package/dist/analysis/data/datasets.js +441 -0
  7. package/dist/analysis/data/describe.d.ts +27 -0
  8. package/dist/analysis/data/describe.js +103 -0
  9. package/dist/analysis/data/engine-client.d.ts +58 -0
  10. package/dist/analysis/data/engine-client.js +304 -0
  11. package/dist/analysis/data/engine-entry.d.ts +1 -0
  12. package/dist/analysis/data/engine-entry.js +59 -0
  13. package/dist/analysis/data/engine-source.d.ts +4 -0
  14. package/dist/analysis/data/engine-source.js +6 -0
  15. package/dist/analysis/data/engine-types.d.ts +86 -0
  16. package/dist/analysis/data/engine-types.js +7 -0
  17. package/dist/analysis/data/infer.d.ts +42 -0
  18. package/dist/analysis/data/infer.js +118 -0
  19. package/dist/analysis/data/ingest-worker.d.ts +17 -0
  20. package/dist/analysis/data/ingest-worker.js +365 -0
  21. package/dist/analysis/data/json.d.ts +19 -0
  22. package/dist/analysis/data/json.js +44 -0
  23. package/dist/analysis/data/query-worker.d.ts +12 -0
  24. package/dist/analysis/data/query-worker.js +75 -0
  25. package/dist/analysis/data/rows.d.ts +39 -0
  26. package/dist/analysis/data/rows.js +120 -0
  27. package/dist/analysis/data/sql-guard.d.ts +20 -0
  28. package/dist/analysis/data/sql-guard.js +129 -0
  29. package/dist/analysis/data/stats.d.ts +18 -0
  30. package/dist/analysis/data/stats.js +77 -0
  31. package/dist/analysis/install-hints.d.ts +33 -0
  32. package/dist/analysis/install-hints.js +144 -0
  33. package/dist/analysis/janitor.d.ts +60 -0
  34. package/dist/analysis/janitor.js +264 -0
  35. package/dist/analysis/jobs.d.ts +94 -0
  36. package/dist/analysis/jobs.js +165 -0
  37. package/dist/analysis/oci.d.ts +129 -0
  38. package/dist/analysis/oci.js +330 -0
  39. package/dist/analysis/python/sources.d.ts +2 -0
  40. package/dist/analysis/python/sources.js +10 -0
  41. package/dist/analysis/requirements.d.ts +32 -0
  42. package/dist/analysis/requirements.js +73 -0
  43. package/dist/analysis/rerun.d.ts +40 -0
  44. package/dist/analysis/rerun.js +136 -0
  45. package/dist/analysis/runtime-manager.d.ts +146 -0
  46. package/dist/analysis/runtime-manager.js +472 -0
  47. package/dist/analysis/status.d.ts +21 -0
  48. package/dist/analysis/status.js +52 -0
  49. package/dist/application.d.ts +41 -0
  50. package/dist/application.js +197 -0
  51. package/dist/artifacts/kinds.d.ts +35 -0
  52. package/dist/artifacts/kinds.js +177 -0
  53. package/dist/artifacts/publisher.d.ts +7 -0
  54. package/dist/artifacts/publisher.js +29 -0
  55. package/dist/artifacts/store.d.ts +119 -0
  56. package/dist/artifacts/store.js +581 -0
  57. package/dist/commands/builtins.js +13 -0
  58. package/dist/config.d.ts +110 -0
  59. package/dist/config.js +186 -8
  60. package/dist/core/contracts.d.ts +69 -1
  61. package/dist/core/runner.d.ts +53 -4
  62. package/dist/core/runner.js +549 -161
  63. package/dist/core/timeout.d.ts +15 -0
  64. package/dist/core/timeout.js +61 -0
  65. package/dist/index.d.ts +23 -4
  66. package/dist/index.js +22 -3
  67. package/dist/mcp/rich.js +4 -0
  68. package/dist/plugins/host.js +14 -6
  69. package/dist/runtime/blobs.d.ts +22 -0
  70. package/dist/runtime/blobs.js +84 -1
  71. package/dist/runtime/ids.d.ts +8 -0
  72. package/dist/runtime/ids.js +32 -0
  73. package/dist/runtime/paths.d.ts +5 -0
  74. package/dist/runtime/paths.js +6 -0
  75. package/dist/runtime/process.d.ts +12 -0
  76. package/dist/runtime/process.js +64 -4
  77. package/dist/runtime/sqlite.d.ts +2 -0
  78. package/dist/runtime/sqlite.js +18 -0
  79. package/dist/runtime/store.d.ts +5 -0
  80. package/dist/runtime/store.js +75 -0
  81. package/dist/runtime/zip.d.ts +24 -0
  82. package/dist/runtime/zip.js +126 -0
  83. package/dist/sessions/children.js +4 -0
  84. package/dist/tools/analysis.d.ts +37 -0
  85. package/dist/tools/analysis.js +572 -0
  86. package/dist/tools/artifacts.d.ts +14 -0
  87. package/dist/tools/artifacts.js +143 -0
  88. package/dist/tools/data.d.ts +7 -0
  89. package/dist/tools/data.js +160 -0
  90. package/dist/tools/execute.js +6 -2
  91. package/package.json +2 -2
@@ -0,0 +1,15 @@
1
+ import type { RunTimeoutInfo } from "@alisio/sdk";
2
+ /** A run stopped by a time limit; the message says which limit, which model and what to do. */
3
+ export declare class RunTimeoutError extends Error {
4
+ readonly info: RunTimeoutInfo;
5
+ constructor(info: RunTimeoutInfo);
6
+ }
7
+ /** `AbortSignal.timeout` aborts with a `TimeoutError`; an explicit `abort()` does not. */
8
+ export declare function isTimeoutReason(reason: unknown): boolean;
9
+ /**
10
+ * A short, human label for a provider id: the host of an OpenAI-compatible endpoint
11
+ * (`openai-compatible:chat:https://api.example.com/v1` → `api.example.com`), else the id itself.
12
+ */
13
+ export declare function providerLabel(id: string | undefined): string | undefined;
14
+ /** Plain-text message of a timeout (TUI, headless, logs, the `runs.error` column). English only. */
15
+ export declare function describeTimeout(info: RunTimeoutInfo): string;
@@ -0,0 +1,61 @@
1
+ /** A run stopped by a time limit; the message says which limit, which model and what to do. */
2
+ export class RunTimeoutError extends Error {
3
+ info;
4
+ constructor(info) {
5
+ super(describeTimeout(info));
6
+ this.name = "RunTimeoutError";
7
+ this.info = info;
8
+ }
9
+ }
10
+ /** `AbortSignal.timeout` aborts with a `TimeoutError`; an explicit `abort()` does not. */
11
+ export function isTimeoutReason(reason) {
12
+ return (typeof reason === "object" &&
13
+ reason !== null &&
14
+ reason.name === "TimeoutError");
15
+ }
16
+ /**
17
+ * A short, human label for a provider id: the host of an OpenAI-compatible endpoint
18
+ * (`openai-compatible:chat:https://api.example.com/v1` → `api.example.com`), else the id itself.
19
+ */
20
+ export function providerLabel(id) {
21
+ if (!id)
22
+ return undefined;
23
+ const url = /(https?:\/\/[^\s]+)/.exec(id)?.[1];
24
+ if (url)
25
+ try {
26
+ return new URL(url).host;
27
+ }
28
+ catch {
29
+ /* fall through to the raw id */
30
+ }
31
+ return id;
32
+ }
33
+ const seconds = (ms) => Math.round(ms / 1000);
34
+ /** Plain-text message of a timeout (TUI, headless, logs, the `runs.error` column). English only. */
35
+ export function describeTimeout(info) {
36
+ const who = info.model
37
+ ? `The model ${info.model}${info.provider ? ` (${info.provider})` : ""}`
38
+ : "The model";
39
+ const limit = `${seconds(info.ms)} s`;
40
+ if (info.kind === "first_token" && info.attempts && info.attempts > 1)
41
+ return (`${who} did not respond after ${info.attempts} attempts of ${limit} each: no tokens were received, so the run was stopped. ` +
42
+ "Retry, switch to another model, or raise or disable limits.firstTokenTimeoutMs (0 disables it); " +
43
+ "limits.firstTokenRetries sets how many times a silent request is sent again.");
44
+ if (info.kind === "first_token")
45
+ return (`${who} sent nothing within ${limit} of the request, so the run was stopped. ` +
46
+ "Retry, switch to another model, or raise or disable limits.firstTokenTimeoutMs (0 disables it).");
47
+ if (info.stage === "waiting_model")
48
+ return info.firstRequest
49
+ ? `${who} did not respond within ${limit}: no tokens were received. ` +
50
+ "Retry, switch to another model, or raise limits.timeoutMs."
51
+ : `The run reached its ${limit} limit while waiting for ${info.model ? `the model ${info.model}` : "the model"}` +
52
+ `${info.provider ? ` (${info.provider})` : ""}. ` +
53
+ "Everything produced so far is kept: prompt again to continue, switch model, or raise limits.timeoutMs.";
54
+ const doing = info.stage === "tool" && info.tool
55
+ ? `while running ${info.tool}`
56
+ : info.stage === "streaming"
57
+ ? "while the model was answering"
58
+ : "";
59
+ return (`The run reached its ${limit} limit${doing ? ` ${doing}` : ""}. ` +
60
+ "Everything produced so far is kept: prompt again to continue, or raise limits.timeoutMs.");
61
+ }
package/dist/index.d.ts CHANGED
@@ -7,11 +7,26 @@ export { AGENT_ID_PATTERN, AGENT_LIMITS, AGENT_REASONING_SUMMARIES, AGENT_TEXT_F
7
7
  export { AGENT_DRAFT_INSTRUCTIONS, AGENT_DRAFT_MAX_DESCRIPTION, type AgentAuthoringGuidance, agentAuthoringGuidance, agentDraftRequest, generateAgentDraft, parseAgentDraft, } from "./agents/draft.ts";
8
8
  export { type AgentChange, AgentDefinitionService, type AgentDefinitionServiceOptions, type AgentRegistry, mergeAgentScopes, } from "./agents/service.ts";
9
9
  export { AGENT_TEMPLATES, agentTemplate } from "./agents/templates.ts";
10
+ export { CapabilityGrants, type GrantRecord, type GrantSource, toGrantWire, } from "./analysis/capabilities.ts";
11
+ export { CsvParser, decodeTextBytes, detectDelimiter } from "./analysis/data/csv.ts";
12
+ export { type DatasetLimits, type DatasetRecord, DatasetService, DEFAULT_DATASET_LIMITS, datasetFormat, PageParamError, toDatasetRef, XLSX_REMEDY, } from "./analysis/data/datasets.ts";
13
+ export { DataEngine, DataError, type DataErrorCode, } from "./analysis/data/engine-client.ts";
14
+ export { guardSql } from "./analysis/data/sql-guard.ts";
15
+ export { detectHintHost, type HintHost, type InstallHints, installHintsMarkdown, installHintsText, parseOsRelease, pythonInstallHints, } from "./analysis/install-hints.ts";
16
+ export { AnalysisJanitor, type RetentionPolicy, SWEEP_INTERVAL_MS, type SweepReport, } from "./analysis/janitor.ts";
17
+ export { AnalysisJobs, type ExecutionDetails, type ExecutionStatus, type JobPaths, } from "./analysis/jobs.ts";
18
+ export { containerName, type OciDeps, type OciEngine, OciRuntime, type OciSettings, type OciStatus, ociRunArgs, parseDigests, } from "./analysis/oci.ts";
19
+ export { AnalysisRerun, type RerunPlan, type RerunPreparation, } from "./analysis/rerun.ts";
20
+ export { AnalysisRuntimeManager, type DiscoveryDeps, type Extras, ExtrasInstallError, type InstallStep, installPreview, type PythonCandidate, type PythonResolution, type RuntimeState, setupCommand, venvInterpreter, } from "./analysis/runtime-manager.ts";
21
+ export { analysisStatus } from "./analysis/status.ts";
10
22
  export { type AppOptions, type BuiltinContext, type BuiltinPlugin, createApplication, enabledBuiltins, type PluginCatalogEntry, type PluginCatalogStatus, } from "./application.ts";
23
+ export { classifyArtifact, extensionOf, fileType as artifactFileType, isPreviewable, KIND_LABELS, viewerContentType, } from "./artifacts/kinds.ts";
24
+ export { createArtifactPublisher } from "./artifacts/publisher.ts";
25
+ export { type ArtifactFile, type ArtifactLimits, type ArtifactOwner, type ArtifactRecord, ArtifactRejected, ArtifactStore, type PublishedArtifact, readHead as readArtifactHead, slugify as artifactSlug, toRef, } from "./artifacts/store.ts";
11
26
  export { BUILTIN_COMMANDS, type BuiltinCommand, CommandCatalog, type CommandExecutionContext, type CommandHost, type CommandResult, type CommandSurface, } from "./commands/catalog.ts";
12
- export { type Config, type ConfigLoadResult, type ConfigProvenance, configFile, configHome, configSchema, isSettableSettingKey, type LoadedConfig, loadConfig, loadConfigWithProvenance, type McpServerSource, overridesSavedProviderProfile, type SettableSettingInfo, type SettableSettingKey, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, settableSettings, stateHome, } from "./config.ts";
27
+ export { type Config, type ConfigLoadResult, type ConfigProvenance, configFile, configHome, configSchema, isSettableSettingKey, type LoadedConfig, loadConfig, loadConfigWithProvenance, type McpServerSource, OCI_IMAGE_PATTERN, overridesSavedProviderProfile, type SettableSettingInfo, type SettableSettingKey, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, settableSettings, stateHome, } from "./config.ts";
13
28
  export { type ContextBudget, checkpointInstructions, completeText, effectiveContextBudget, estimateTokens, MAX_TRUSTED_WINDOW, parseCheckpointOutput, planCompaction, renderCheckpoint, serializeForSummary, shouldCompact, shouldCompactContext, summarize, } from "./core/compaction.ts";
14
- export type { ApprovalDecision, ApprovalHandler, ApprovalRequest, BeginRunInput, ContextSource, EndRunInput, EventPage, HookFailure, MessagePage, PageOptions, Policy, RunnerExtensions, RunRecord, RunStatus, Session, SessionStore, StoredEvent, TerminalRunStatus, ToolCallMeta, } from "./core/contracts.ts";
29
+ export type { ApprovalDecision, ApprovalHandler, ApprovalRequest, ArtifactPublisherFactory, BeginRunInput, CapabilityGate, ContextSource, CoreArtifactPublisher, EndRunInput, EventPage, HookFailure, MessagePage, PageOptions, Policy, PublishedArtifactInfo, RunnerExtensions, RunRecord, RunStatus, Session, SessionStore, StoredEvent, TerminalRunStatus, ToolCallMeta, } from "./core/contracts.ts";
15
30
  export { ToolRegistry } from "./core/registry.ts";
16
31
  export { AgentRunner, type CompactionResult, defaultTokenBudget, type RunnerOptions, type RunnerSettingsPatch, type RunOptions, } from "./core/runner.ts";
17
32
  export { type ExtensionConflict, ExtensionRegistry } from "./extensions/registry.ts";
@@ -26,16 +41,20 @@ export { type InstructionFile, ProjectContext, type ProjectContextOptions, } fro
26
41
  export { expandSlashPrompt, loadPromptTemplates, type PromptDiagnostic, type PromptRequirement, type PromptSource, type PromptSourceKind, type PromptTemplate, parsePromptTemplate, promptSources, renderPromptTemplate, splitArguments, } from "./resources/prompts.ts";
27
42
  export { type Skill, type SkillCatalogEntry, type SkillRoot, type SkillScope, Skills, skillRoots, } from "./resources/skills.ts";
28
43
  export { type ExternalDirectoryDecision, type ExternalDirectoryHandler, type ExternalDirectoryRequest, PathAccess, type PathAccessOptions, type ResolvePathOptions, } from "./runtime/access.ts";
29
- export { BlobStore, type BlobStoreOptions } from "./runtime/blobs.ts";
44
+ export { BlobStore, type BlobStoreOptions, BlobTooLarge } from "./runtime/blobs.ts";
30
45
  export { clipLines, unifiedPatch } from "./runtime/diff.ts";
31
46
  export { exists, fileSize, readHead, readJson, readText, which } from "./runtime/fs.ts";
47
+ export { newId, ulid } from "./runtime/ids.ts";
32
48
  export { defaultGlobalRoots, isPathSpec, PLUGIN_KEYWORD, resolvePluginSpec, } from "./runtime/modules.ts";
33
- export { findWorkspace, inside, outsideRootsMessage, safePath } from "./runtime/paths.ts";
49
+ export { findWorkspace, inside, outsideRootsMessage, safePath, workspaceKey, } from "./runtime/paths.ts";
34
50
  export { isMissingCommand, type ProcessResult, RIPGREP_INSTALL_HINT, runProcess, } from "./runtime/process.ts";
35
51
  export { isSqliteExperimentalWarning, openDatabase } from "./runtime/sqlite.ts";
36
52
  export { SQLiteStore } from "./runtime/store.ts";
53
+ export { crc32, writeZip, type ZipEntry, type ZipSink } from "./runtime/zip.ts";
37
54
  export { ChildSessions, type ChildSessionsOptions } from "./sessions/children.ts";
38
55
  export { formatSideQuestion, SIDE_QUESTION_DESCRIPTION, SIDE_QUESTION_HISTORY_LIMIT, SIDE_QUESTION_INSTRUCTIONS, SIDE_QUESTION_MAX_CHARS, SIDE_QUESTION_USAGE, SIDE_QUESTIONS_NAMESPACE, type SideQuestionEntry, SideQuestions, type SideQuestionsOptions, sideTranscript, } from "./sessions/side-questions.ts";
39
56
  export { composeSideBySide, DefaultAlisioMascot, DefaultStartupScreen, infoSection, mascotSection, padEnd, pluginsSection, renderStartup, type StartupDiagnostic, type StartupInput, type StartupResult, sanitizeLine, shortenPath, startupTips, stripAnsi, tipsSection, titleSection, truncate, visibleWidth, welcomeSection, } from "./startup/index.ts";
57
+ export { formatBytes, NOT_SANDBOXED } from "./tools/analysis.ts";
58
+ export { ARTIFACT_READ_MAX, exportedPaths } from "./tools/artifacts.ts";
40
59
  export { hash, objectSchema, registerStandard } from "./tools/standard.ts";
41
60
  export { getTrust, hashProjectConfig, hasProjectResources, listTrust, resolveTrust, revokeTrust, setTrust, type TrustEntry, type TrustListing, type TrustResolution, trustStorePath, } from "./trust.ts";
package/dist/index.js CHANGED
@@ -7,9 +7,24 @@ export { AGENT_ID_PATTERN, AGENT_LIMITS, AGENT_REASONING_SUMMARIES, AGENT_TEXT_F
7
7
  export { AGENT_DRAFT_INSTRUCTIONS, AGENT_DRAFT_MAX_DESCRIPTION, agentAuthoringGuidance, agentDraftRequest, generateAgentDraft, parseAgentDraft, } from "./agents/draft.js";
8
8
  export { AgentDefinitionService, mergeAgentScopes, } from "./agents/service.js";
9
9
  export { AGENT_TEMPLATES, agentTemplate } from "./agents/templates.js";
10
+ export { CapabilityGrants, toGrantWire, } from "./analysis/capabilities.js";
11
+ export { CsvParser, decodeTextBytes, detectDelimiter } from "./analysis/data/csv.js";
12
+ export { DatasetService, DEFAULT_DATASET_LIMITS, datasetFormat, PageParamError, toDatasetRef, XLSX_REMEDY, } from "./analysis/data/datasets.js";
13
+ export { DataEngine, DataError, } from "./analysis/data/engine-client.js";
14
+ export { guardSql } from "./analysis/data/sql-guard.js";
15
+ export { detectHintHost, installHintsMarkdown, installHintsText, parseOsRelease, pythonInstallHints, } from "./analysis/install-hints.js";
16
+ export { AnalysisJanitor, SWEEP_INTERVAL_MS, } from "./analysis/janitor.js";
17
+ export { AnalysisJobs, } from "./analysis/jobs.js";
18
+ export { containerName, OciRuntime, ociRunArgs, parseDigests, } from "./analysis/oci.js";
19
+ export { AnalysisRerun, } from "./analysis/rerun.js";
20
+ export { AnalysisRuntimeManager, ExtrasInstallError, installPreview, setupCommand, venvInterpreter, } from "./analysis/runtime-manager.js";
21
+ export { analysisStatus } from "./analysis/status.js";
10
22
  export { createApplication, enabledBuiltins, } from "./application.js";
23
+ export { classifyArtifact, extensionOf, fileType as artifactFileType, isPreviewable, KIND_LABELS, viewerContentType, } from "./artifacts/kinds.js";
24
+ export { createArtifactPublisher } from "./artifacts/publisher.js";
25
+ export { ArtifactRejected, ArtifactStore, readHead as readArtifactHead, slugify as artifactSlug, toRef, } from "./artifacts/store.js";
11
26
  export { BUILTIN_COMMANDS, CommandCatalog, } from "./commands/catalog.js";
12
- export { configFile, configHome, configSchema, isSettableSettingKey, loadConfig, loadConfigWithProvenance, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, settableSettings, stateHome, } from "./config.js";
27
+ export { configFile, configHome, configSchema, isSettableSettingKey, loadConfig, loadConfigWithProvenance, OCI_IMAGE_PATTERN, overridesSavedProviderProfile, setConfigValue, setGlobalMcpAllow, setMcpServerEnabled, setProjectPluginEnabled, setProjectSkillEnabled, settableSettings, stateHome, } from "./config.js";
13
28
  export { checkpointInstructions, completeText, effectiveContextBudget, estimateTokens, MAX_TRUSTED_WINDOW, parseCheckpointOutput, planCompaction, renderCheckpoint, serializeForSummary, shouldCompact, shouldCompactContext, summarize, } from "./core/compaction.js";
14
29
  export { ToolRegistry } from "./core/registry.js";
15
30
  export { AgentRunner, defaultTokenBudget, } from "./core/runner.js";
@@ -25,16 +40,20 @@ export { ProjectContext, } from "./resources/context.js";
25
40
  export { expandSlashPrompt, loadPromptTemplates, parsePromptTemplate, promptSources, renderPromptTemplate, splitArguments, } from "./resources/prompts.js";
26
41
  export { Skills, skillRoots, } from "./resources/skills.js";
27
42
  export { PathAccess, } from "./runtime/access.js";
28
- export { BlobStore } from "./runtime/blobs.js";
43
+ export { BlobStore, BlobTooLarge } from "./runtime/blobs.js";
29
44
  export { clipLines, unifiedPatch } from "./runtime/diff.js";
30
45
  export { exists, fileSize, readHead, readJson, readText, which } from "./runtime/fs.js";
46
+ export { newId, ulid } from "./runtime/ids.js";
31
47
  export { defaultGlobalRoots, isPathSpec, PLUGIN_KEYWORD, resolvePluginSpec, } from "./runtime/modules.js";
32
- export { findWorkspace, inside, outsideRootsMessage, safePath } from "./runtime/paths.js";
48
+ export { findWorkspace, inside, outsideRootsMessage, safePath, workspaceKey, } from "./runtime/paths.js";
33
49
  export { isMissingCommand, RIPGREP_INSTALL_HINT, runProcess, } from "./runtime/process.js";
34
50
  export { isSqliteExperimentalWarning, openDatabase } from "./runtime/sqlite.js";
35
51
  export { SQLiteStore } from "./runtime/store.js";
52
+ export { crc32, writeZip } from "./runtime/zip.js";
36
53
  export { ChildSessions } from "./sessions/children.js";
37
54
  export { formatSideQuestion, SIDE_QUESTION_DESCRIPTION, SIDE_QUESTION_HISTORY_LIMIT, SIDE_QUESTION_INSTRUCTIONS, SIDE_QUESTION_MAX_CHARS, SIDE_QUESTION_USAGE, SIDE_QUESTIONS_NAMESPACE, SideQuestions, sideTranscript, } from "./sessions/side-questions.js";
38
55
  export { composeSideBySide, DefaultAlisioMascot, DefaultStartupScreen, infoSection, mascotSection, padEnd, pluginsSection, renderStartup, sanitizeLine, shortenPath, startupTips, stripAnsi, tipsSection, titleSection, truncate, visibleWidth, welcomeSection, } from "./startup/index.js";
56
+ export { formatBytes, NOT_SANDBOXED } from "./tools/analysis.js";
57
+ export { ARTIFACT_READ_MAX, exportedPaths } from "./tools/artifacts.js";
39
58
  export { hash, objectSchema, registerStandard } from "./tools/standard.js";
40
59
  export { getTrust, hashProjectConfig, hasProjectResources, listTrust, resolveTrust, revokeTrust, setTrust, trustStorePath, } from "./trust.js";
package/dist/mcp/rich.js CHANGED
@@ -227,6 +227,10 @@ export function renderUiBlockText(block) {
227
227
  block.title ? `[progress: ${block.title}]` : "[progress]",
228
228
  ...block.steps.map((step) => `- [${step.status}] ${step.label}${step.detail ? ` (${step.detail})` : ""}`),
229
229
  ].join("\n");
230
+ case "artifact": {
231
+ const a = block.artifact;
232
+ return `[artifact: ${a.fileName} · ${a.kind} · ${a.bytes} bytes · ${a.id}${a.partial ? " · partial" : ""}${a.status !== "ready" ? ` · ${a.status}` : ""}]`;
233
+ }
230
234
  default: {
231
235
  // A kind this build does not know (e.g. persisted by a newer Alisio): keep it readable.
232
236
  const kind = String(block.kind ?? "unknown");
@@ -203,12 +203,20 @@ export class PluginHost {
203
203
  register: (tool) => {
204
204
  const registered = builtin
205
205
  ? tool
206
- : {
207
- ...tool,
208
- name: `${pluginPrefix(plugin.id)}_${tool.name}`,
209
- // Only built-ins may claim the always-allowed internal effect.
210
- ...(tool.effect === "internal" ? { effect: "external" } : {}),
211
- };
206
+ : (() => {
207
+ // Only built-ins may claim the always-allowed internal effect, a capability
208
+ // (finer than the effect) or the artifact publisher (decision D12, v1).
209
+ const { capability: _capability, ...rest } = tool;
210
+ return {
211
+ ...rest,
212
+ name: `${pluginPrefix(plugin.id)}_${tool.name}`,
213
+ ...(tool.effect === "internal" ? { effect: "external" } : {}),
214
+ execute: (input, context) => {
215
+ const { artifacts: _artifacts, approveInstall: _install, ...narrowed } = context;
216
+ return tool.execute(input, narrowed);
217
+ },
218
+ };
219
+ })();
212
220
  const unregister = this.registry.register(registered);
213
221
  this.toolOwners.set(registered.name, plugin.id);
214
222
  return track(() => {
@@ -1,4 +1,9 @@
1
1
  import type { Attachment, BlobRef, SqlDatabase } from "@alisio/sdk";
2
+ /** A streamed blob exceeded its size limit. */
3
+ export declare class BlobTooLarge extends Error {
4
+ readonly limit: number;
5
+ constructor(limit: number);
6
+ }
2
7
  export interface BlobStoreOptions {
3
8
  /** Directory that holds `sha256/…` (by default `<state home>/blobs`). */
4
9
  root: string;
@@ -17,6 +22,23 @@ export declare class BlobStore {
17
22
  width?: number;
18
23
  height?: number;
19
24
  }): BlobRef;
25
+ /**
26
+ * Stores a byte stream (an upload too large to hold in memory, e.g. a dataset) once per content
27
+ * hash, hashing while it writes. Fails with `BlobTooLarge` as soon as `maxBytes` is exceeded and
28
+ * leaves nothing behind. The stored type is the declared one; images stay the only blobs that
29
+ * `attachment()` and the blob route serve.
30
+ */
31
+ putStream(stream: AsyncIterable<Uint8Array>, mimeType: string, options: {
32
+ maxBytes: number;
33
+ }): Promise<BlobRef>;
34
+ /**
35
+ * Retention: deletes the bytes and the row of a blob that is not an image (an uploaded dataset
36
+ * original). Images are referenced by messages and are never removed here. Returns the freed
37
+ * bytes (0 when the blob is unknown, an image or already gone).
38
+ */
39
+ removeUpload(hash: string): number;
40
+ /** Hashes of non-image blobs created before `cutoff` (epoch ms): uploaded dataset originals. */
41
+ uploadsBefore(cutoff: number): string[];
20
42
  get(hash: string): {
21
43
  bytes: Buffer;
22
44
  mimeType: string;
@@ -8,9 +8,18 @@
8
8
  * Persisted messages therefore still carry base64 (as legacy inline attachments do).
9
9
  */
10
10
  import { createHash, randomUUID } from "node:crypto";
11
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
11
+ import { chmodSync, createWriteStream, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
12
12
  import { join } from "node:path";
13
+ import { pipeline } from "node:stream/promises";
13
14
  const HASH = /^[0-9a-f]{64}$/;
15
+ /** A streamed blob exceeded its size limit. */
16
+ export class BlobTooLarge extends Error {
17
+ limit;
18
+ constructor(limit) {
19
+ super(`The upload exceeds ${limit} bytes`);
20
+ this.limit = limit;
21
+ }
22
+ }
14
23
  export class BlobStore {
15
24
  options;
16
25
  constructor(options) {
@@ -61,6 +70,80 @@ export class BlobStore {
61
70
  ...(row?.height != null ? { height: Number(row.height) } : {}),
62
71
  };
63
72
  }
73
+ /**
74
+ * Stores a byte stream (an upload too large to hold in memory, e.g. a dataset) once per content
75
+ * hash, hashing while it writes. Fails with `BlobTooLarge` as soon as `maxBytes` is exceeded and
76
+ * leaves nothing behind. The stored type is the declared one; images stay the only blobs that
77
+ * `attachment()` and the blob route serve.
78
+ */
79
+ async putStream(stream, mimeType, options) {
80
+ const mime = mimeType.trim();
81
+ if (!mime || mime.length > 255)
82
+ throw new Error("A blob needs a MIME type");
83
+ const dir = join(this.options.root, "sha256");
84
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
85
+ const temp = join(dir, `.tmp-${randomUUID()}`);
86
+ const hash = createHash("sha256");
87
+ let size = 0;
88
+ try {
89
+ await pipeline(stream, async function* (source) {
90
+ for await (const chunk of source) {
91
+ size += chunk.byteLength;
92
+ if (size > options.maxBytes)
93
+ throw new BlobTooLarge(options.maxBytes);
94
+ hash.update(chunk);
95
+ yield chunk;
96
+ }
97
+ }, createWriteStream(temp, { mode: 0o600, flags: "wx" }));
98
+ const digest = hash.digest("hex");
99
+ const file = this.path(digest);
100
+ const fileDir = join(dir, digest.slice(0, 2));
101
+ mkdirSync(fileDir, { recursive: true, mode: 0o700 });
102
+ chmodSync(fileDir, 0o700);
103
+ if (existsSync(file))
104
+ rmSync(temp, { force: true });
105
+ else
106
+ renameSync(temp, file);
107
+ this.options.db
108
+ .prepare("INSERT OR IGNORE INTO blobs(hash,mime,size,width,height,created_at) VALUES(?,?,?,?,?,?)")
109
+ .run(digest, mime, statSync(file).size, null, null, Date.now());
110
+ const row = this.row(digest);
111
+ return { hash: digest, mimeType: row?.mime ?? mime, bytes: statSync(file).size };
112
+ }
113
+ catch (error) {
114
+ rmSync(temp, { force: true });
115
+ throw error;
116
+ }
117
+ }
118
+ /**
119
+ * Retention: deletes the bytes and the row of a blob that is not an image (an uploaded dataset
120
+ * original). Images are referenced by messages and are never removed here. Returns the freed
121
+ * bytes (0 when the blob is unknown, an image or already gone).
122
+ */
123
+ removeUpload(hash) {
124
+ if (!HASH.test(hash))
125
+ return 0;
126
+ const row = this.row(hash);
127
+ if (row?.mime.startsWith("image/"))
128
+ return 0;
129
+ const file = this.path(hash);
130
+ let size = 0;
131
+ try {
132
+ size = statSync(file).size;
133
+ rmSync(file, { force: true });
134
+ }
135
+ catch {
136
+ /* Already gone. */
137
+ }
138
+ this.options.db.prepare("DELETE FROM blobs WHERE hash=?").run(hash);
139
+ return size;
140
+ }
141
+ /** Hashes of non-image blobs created before `cutoff` (epoch ms): uploaded dataset originals. */
142
+ uploadsBefore(cutoff) {
143
+ return this.options.db
144
+ .prepare("SELECT hash FROM blobs WHERE mime NOT LIKE 'image/%' AND created_at<?")
145
+ .all(cutoff).map((r) => r.hash);
146
+ }
64
147
  get(hash) {
65
148
  if (!HASH.test(hash))
66
149
  return undefined;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A ULID (26 characters, lexicographically sortable by creation time): 48 bits of milliseconds
3
+ * and 80 random bits. No dependency and no monotonic counter; two ids created in the same
4
+ * millisecond sort arbitrarily, which is fine for row ids and folder names.
5
+ */
6
+ export declare function ulid(now?: number): string;
7
+ /** A prefixed id such as `art_01J…` or `exec_01J…`. */
8
+ export declare const newId: (prefix: string, now?: number) => string;
@@ -0,0 +1,32 @@
1
+ import { randomBytes } from "node:crypto";
2
+ /** Crockford base32 (no I, L, O, U), the ULID alphabet. */
3
+ const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
4
+ /**
5
+ * A ULID (26 characters, lexicographically sortable by creation time): 48 bits of milliseconds
6
+ * and 80 random bits. No dependency and no monotonic counter; two ids created in the same
7
+ * millisecond sort arbitrarily, which is fine for row ids and folder names.
8
+ */
9
+ export function ulid(now = Date.now()) {
10
+ let time = "";
11
+ let t = Math.max(0, Math.floor(now));
12
+ for (let i = 0; i < 10; i++) {
13
+ time = ALPHABET[t % 32] + time;
14
+ t = Math.floor(t / 32);
15
+ }
16
+ const bytes = randomBytes(10);
17
+ let random = "";
18
+ // 80 bits → 16 characters of 5 bits each.
19
+ let buffer = 0, bits = 0;
20
+ for (const byte of bytes) {
21
+ buffer = (buffer << 8) | byte;
22
+ bits += 8;
23
+ while (bits >= 5) {
24
+ bits -= 5;
25
+ random += ALPHABET[(buffer >> bits) & 31];
26
+ }
27
+ buffer &= (1 << bits) - 1;
28
+ }
29
+ return time + random;
30
+ }
31
+ /** A prefixed id such as `art_01J…` or `exec_01J…`. */
32
+ export const newId = (prefix, now) => `${prefix}_${ulid(now)}`;
@@ -8,3 +8,8 @@ export declare function outsideRootsMessage(target: string, reason?: string): st
8
8
  /** Mediated path policy; not an OS sandbox and not race-proof against hostile concurrent processes. */
9
9
  export declare function safePath(root: string, path: string): Promise<string>;
10
10
  export declare function findWorkspace(cwd: string): Promise<string>;
11
+ /**
12
+ * Opaque, stable key of a workspace path: the first 16 hex characters of its sha256. Core names
13
+ * per-workspace folders with it and the server exposes it as the web `workspaceId`.
14
+ */
15
+ export declare const workspaceKey: (path: string) => string;
@@ -1,3 +1,4 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { lstat, realpath } from "node:fs/promises";
2
3
  import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
3
4
  export function inside(root, path) {
@@ -50,3 +51,8 @@ export async function findWorkspace(cwd) {
50
51
  path = dirname(path);
51
52
  }
52
53
  }
54
+ /**
55
+ * Opaque, stable key of a workspace path: the first 16 hex characters of its sha256. Core names
56
+ * per-workspace folders with it and the server exposes it as the web `workspaceId`.
57
+ */
58
+ export const workspaceKey = (path) => createHash("sha256").update(path).digest("hex").slice(0, 16);
@@ -22,4 +22,16 @@ export declare function runProcess(command: string, args: string[], options: {
22
22
  onData?: (chunk: string) => void;
23
23
  /** On cancellation: SIGTERM the process group, then SIGKILL after this grace (default 5s). */
24
24
  killGraceMs?: number;
25
+ /**
26
+ * What happens when a stream exceeds `maxBytes`: `kill` (default) stops the process; `truncate`
27
+ * lets it run and keeps only the LAST `maxBytes` of each stream (the result is `truncated`).
28
+ */
29
+ onOverflow?: "kill" | "truncate";
30
+ /** Full stdout/stderr copies (up to `maxLogBytes` each), independent of `maxBytes`. */
31
+ logFiles?: {
32
+ stdout: string;
33
+ stderr: string;
34
+ };
35
+ /** Cap of each log file (default 10 MiB); a marker line notes the cut. */
36
+ maxLogBytes?: number;
25
37
  }): Promise<ProcessResult>;
@@ -1,4 +1,5 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
+ import { createWriteStream } from "node:fs";
2
3
  /**
3
4
  * Detect a spawn failure caused by a missing executable (`ENOENT`), so tools can explain how to
4
5
  * install the dependency instead of surfacing the raw OS error.
@@ -76,19 +77,77 @@ export async function runProcess(command, args, options) {
76
77
  terminate();
77
78
  let size = 0, truncated = false;
78
79
  const max = options.maxBytes ?? 32_000;
79
- const consume = (stream) => new Promise((resolveText, reject) => {
80
+ const tailMode = options.onOverflow === "truncate";
81
+ const maxLog = options.maxLogBytes ?? 10 * 1024 * 1024;
82
+ const logs = [];
83
+ const logger = (path) => {
84
+ if (!path)
85
+ return undefined;
86
+ const out = createWriteStream(path, { flags: "w" });
87
+ out.on("error", () => {
88
+ /* A log write failure never fails the process run. */
89
+ });
90
+ logs.push(out);
91
+ let written = 0, cut = false;
92
+ return (chunk) => {
93
+ if (cut)
94
+ return;
95
+ const room = maxLog - written;
96
+ if (chunk.byteLength > room) {
97
+ out.write(chunk.subarray(0, Math.max(0, room)));
98
+ out.write(Buffer.from("\n[log truncated]\n"));
99
+ cut = true;
100
+ written = maxLog;
101
+ return;
102
+ }
103
+ out.write(chunk);
104
+ written += chunk.byteLength;
105
+ };
106
+ };
107
+ const consume = (stream, logPath) => new Promise((resolveText, reject) => {
80
108
  const decoder = new TextDecoder();
109
+ const log = logger(logPath);
81
110
  let text = "", done = false;
111
+ // Tail mode keeps raw buffers and decodes once at the end (a cut may split a character).
112
+ const tail = [];
113
+ let tailBytes = 0;
82
114
  const finish = () => {
83
115
  if (done)
84
116
  return;
85
117
  done = true;
86
- text += decoder.decode();
118
+ if (tailMode) {
119
+ const joined = Buffer.concat(tail);
120
+ text = new TextDecoder().decode(joined).replace(/^\uFFFD+/, "");
121
+ }
122
+ else
123
+ text += decoder.decode();
87
124
  resolveText(text);
88
125
  };
89
126
  stream.on("data", (value) => {
90
127
  if (done)
91
128
  return;
129
+ log?.(value);
130
+ if (tailMode) {
131
+ const live = decoder.decode(value, { stream: true });
132
+ if (live)
133
+ options.onData?.(live);
134
+ tail.push(value);
135
+ tailBytes += value.byteLength;
136
+ while (tailBytes > max && tail.length) {
137
+ const first = tail[0];
138
+ const excess = tailBytes - max;
139
+ if (first.byteLength <= excess) {
140
+ tail.shift();
141
+ tailBytes -= first.byteLength;
142
+ }
143
+ else {
144
+ tail[0] = first.subarray(excess);
145
+ tailBytes -= excess;
146
+ }
147
+ truncated = true;
148
+ }
149
+ return;
150
+ }
92
151
  const remaining = Math.max(0, max - size), slice = value.subarray(0, remaining);
93
152
  size += slice.byteLength;
94
153
  const chunk = decoder.decode(slice, { stream: true });
@@ -107,8 +166,8 @@ export async function runProcess(command, args, options) {
107
166
  });
108
167
  try {
109
168
  const [stdout, stderr, exitCode] = await Promise.all([
110
- consume(child.stdout),
111
- consume(child.stderr),
169
+ consume(child.stdout, options.logFiles?.stdout),
170
+ consume(child.stderr, options.logFiles?.stderr),
112
171
  exited,
113
172
  ]);
114
173
  signal.throwIfAborted();
@@ -118,5 +177,6 @@ export async function runProcess(command, args, options) {
118
177
  signal.removeEventListener("abort", terminate);
119
178
  clearTimeout(graceTimer);
120
179
  kill();
180
+ await Promise.all(logs.map((out) => new Promise((resolveLog) => out.end(() => resolveLog()))));
121
181
  }
122
182
  }
@@ -1,3 +1,5 @@
1
1
  import type { SqlDatabase } from "@alisio/sdk";
2
2
  export declare function isSqliteExperimentalWarning(message: string, type?: string): boolean;
3
+ /** Opens an existing database file read-only (no directory or file is created). */
4
+ export declare function openReadOnlyDatabase(path: string): SqlDatabase;
3
5
  export declare function openDatabase(path: string): SqlDatabase;
@@ -34,6 +34,24 @@ function sqlite() {
34
34
  return module;
35
35
  }
36
36
  const plain = (row) => row === undefined || row === null ? undefined : { ...row };
37
+ /** Opens an existing database file read-only (no directory or file is created). */
38
+ export function openReadOnlyDatabase(path) {
39
+ const db = new (sqlite().DatabaseSync)(path, { readOnly: true });
40
+ db.exec("PRAGMA query_only=ON;");
41
+ return {
42
+ exec: (sql) => db.exec(sql),
43
+ prepare(sql) {
44
+ const raw = db.prepare(sql);
45
+ return {
46
+ run: (...params) => raw.run(...params),
47
+ get: (...params) => plain(raw.get(...params)),
48
+ all: (...params) => raw.all(...params).map((r) => ({ ...r })),
49
+ };
50
+ },
51
+ transaction: (fn) => fn(),
52
+ close: () => db.close(),
53
+ };
54
+ }
37
55
  export function openDatabase(path) {
38
56
  const memory = path === ":memory:";
39
57
  const fresh = !memory && !existsSync(path);
@@ -5,6 +5,11 @@ export declare class SQLiteStore implements SessionStore {
5
5
  constructor(path: string);
6
6
  /** Forward-only, idempotent migrations. Existing databases keep all rows. */
7
7
  private migrate;
8
+ /**
9
+ * The root of a session (itself for roots), following `parent_id`. Unknown ids are their own
10
+ * root, so callers never throw on a session another process has not written yet.
11
+ */
12
+ rootOf(id: string): string;
8
13
  create(workspace: string, provider: string, model: string): Session;
9
14
  private row;
10
15
  get(id: string): Session;