@alisio/core 0.1.0-alpha.20 → 0.1.0-alpha.22

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 (104) hide show
  1. package/dist/agents/active.d.ts +19 -0
  2. package/dist/agents/active.js +41 -0
  3. package/dist/agents/capabilities.d.ts +26 -0
  4. package/dist/agents/capabilities.js +120 -0
  5. package/dist/agents/create-agent-skill.d.ts +11 -0
  6. package/dist/agents/create-agent-skill.js +60 -0
  7. package/dist/agents/definitions.d.ts +88 -0
  8. package/dist/agents/definitions.js +404 -0
  9. package/dist/agents/draft.d.ts +41 -0
  10. package/dist/agents/draft.js +133 -0
  11. package/dist/agents/service.d.ts +57 -0
  12. package/dist/agents/service.js +86 -0
  13. package/dist/agents/templates.d.ts +8 -0
  14. package/dist/agents/templates.js +102 -0
  15. package/dist/analysis/capabilities.d.ts +48 -0
  16. package/dist/analysis/capabilities.js +90 -0
  17. package/dist/analysis/data/csv.d.ts +46 -0
  18. package/dist/analysis/data/csv.js +240 -0
  19. package/dist/analysis/data/datasets.d.ts +132 -0
  20. package/dist/analysis/data/datasets.js +441 -0
  21. package/dist/analysis/data/describe.d.ts +27 -0
  22. package/dist/analysis/data/describe.js +103 -0
  23. package/dist/analysis/data/engine-client.d.ts +58 -0
  24. package/dist/analysis/data/engine-client.js +304 -0
  25. package/dist/analysis/data/engine-entry.d.ts +1 -0
  26. package/dist/analysis/data/engine-entry.js +59 -0
  27. package/dist/analysis/data/engine-source.d.ts +4 -0
  28. package/dist/analysis/data/engine-source.js +6 -0
  29. package/dist/analysis/data/engine-types.d.ts +86 -0
  30. package/dist/analysis/data/engine-types.js +7 -0
  31. package/dist/analysis/data/infer.d.ts +42 -0
  32. package/dist/analysis/data/infer.js +118 -0
  33. package/dist/analysis/data/ingest-worker.d.ts +17 -0
  34. package/dist/analysis/data/ingest-worker.js +365 -0
  35. package/dist/analysis/data/json.d.ts +19 -0
  36. package/dist/analysis/data/json.js +44 -0
  37. package/dist/analysis/data/query-worker.d.ts +12 -0
  38. package/dist/analysis/data/query-worker.js +75 -0
  39. package/dist/analysis/data/rows.d.ts +39 -0
  40. package/dist/analysis/data/rows.js +120 -0
  41. package/dist/analysis/data/sql-guard.d.ts +20 -0
  42. package/dist/analysis/data/sql-guard.js +129 -0
  43. package/dist/analysis/data/stats.d.ts +18 -0
  44. package/dist/analysis/data/stats.js +77 -0
  45. package/dist/analysis/install-hints.d.ts +33 -0
  46. package/dist/analysis/install-hints.js +144 -0
  47. package/dist/analysis/janitor.d.ts +60 -0
  48. package/dist/analysis/janitor.js +264 -0
  49. package/dist/analysis/jobs.d.ts +94 -0
  50. package/dist/analysis/jobs.js +165 -0
  51. package/dist/analysis/oci.d.ts +129 -0
  52. package/dist/analysis/oci.js +330 -0
  53. package/dist/analysis/python/sources.d.ts +2 -0
  54. package/dist/analysis/python/sources.js +10 -0
  55. package/dist/analysis/requirements.d.ts +32 -0
  56. package/dist/analysis/requirements.js +73 -0
  57. package/dist/analysis/rerun.d.ts +40 -0
  58. package/dist/analysis/rerun.js +136 -0
  59. package/dist/analysis/runtime-manager.d.ts +146 -0
  60. package/dist/analysis/runtime-manager.js +472 -0
  61. package/dist/analysis/status.d.ts +21 -0
  62. package/dist/analysis/status.js +52 -0
  63. package/dist/application.d.ts +48 -0
  64. package/dist/application.js +228 -0
  65. package/dist/artifacts/kinds.d.ts +35 -0
  66. package/dist/artifacts/kinds.js +177 -0
  67. package/dist/artifacts/publisher.d.ts +7 -0
  68. package/dist/artifacts/publisher.js +29 -0
  69. package/dist/artifacts/store.d.ts +119 -0
  70. package/dist/artifacts/store.js +581 -0
  71. package/dist/commands/builtins.js +13 -0
  72. package/dist/commands/catalog.js +13 -1
  73. package/dist/config.d.ts +102 -0
  74. package/dist/config.js +167 -6
  75. package/dist/core/contracts.d.ts +69 -1
  76. package/dist/core/runner.d.ts +34 -4
  77. package/dist/core/runner.js +388 -130
  78. package/dist/index.d.ts +30 -5
  79. package/dist/index.js +29 -4
  80. package/dist/mcp/rich.js +4 -0
  81. package/dist/plugins/host.js +14 -6
  82. package/dist/runtime/blobs.d.ts +22 -0
  83. package/dist/runtime/blobs.js +84 -1
  84. package/dist/runtime/ids.d.ts +8 -0
  85. package/dist/runtime/ids.js +32 -0
  86. package/dist/runtime/paths.d.ts +5 -0
  87. package/dist/runtime/paths.js +6 -0
  88. package/dist/runtime/process.d.ts +12 -0
  89. package/dist/runtime/process.js +64 -4
  90. package/dist/runtime/sqlite.d.ts +2 -0
  91. package/dist/runtime/sqlite.js +18 -0
  92. package/dist/runtime/store.d.ts +5 -0
  93. package/dist/runtime/store.js +75 -0
  94. package/dist/runtime/zip.d.ts +24 -0
  95. package/dist/runtime/zip.js +126 -0
  96. package/dist/sessions/children.js +4 -0
  97. package/dist/tools/analysis.d.ts +37 -0
  98. package/dist/tools/analysis.js +572 -0
  99. package/dist/tools/artifacts.d.ts +14 -0
  100. package/dist/tools/artifacts.js +143 -0
  101. package/dist/tools/data.d.ts +7 -0
  102. package/dist/tools/data.js +160 -0
  103. package/dist/tools/execute.js +6 -2
  104. package/package.json +2 -2
package/dist/config.d.ts CHANGED
@@ -12,6 +12,11 @@ declare const serverSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
12
12
  url: z.ZodString;
13
13
  bearerTokenEnv: z.ZodOptional<z.ZodString>;
14
14
  }, z.core.$strict>], "transport">;
15
+ /**
16
+ * A container image reference pinned by digest. Anchored at the start with a letter or digit so a
17
+ * value can never be read as an option by the container CLI.
18
+ */
19
+ export declare const OCI_IMAGE_PATTERN: RegExp;
15
20
  export declare const configSchema: z.ZodPipe<z.ZodObject<{
16
21
  schemaVersion: z.ZodDefault<z.ZodLiteral<1>>;
17
22
  provider: z.ZodDefault<z.ZodObject<{
@@ -174,6 +179,40 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
174
179
  maxOutputTokens: z.ZodDefault<z.ZodNumber>;
175
180
  maxTokens: z.ZodOptional<z.ZodNumber>;
176
181
  }, z.core.$strip>>;
182
+ analysis: z.ZodDefault<z.ZodObject<{
183
+ enabled: z.ZodDefault<z.ZodBoolean>;
184
+ runtime: z.ZodDefault<z.ZodEnum<{
185
+ managed: "managed";
186
+ oci: "oci";
187
+ }>>;
188
+ oci: z.ZodDefault<z.ZodObject<{
189
+ engine: z.ZodDefault<z.ZodEnum<{
190
+ docker: "docker";
191
+ podman: "podman";
192
+ }>>;
193
+ image: z.ZodOptional<z.ZodString>;
194
+ memoryMb: z.ZodDefault<z.ZodNumber>;
195
+ cpus: z.ZodDefault<z.ZodNumber>;
196
+ }, z.core.$strict>>;
197
+ limits: z.ZodDefault<z.ZodObject<{
198
+ timeoutMs: z.ZodDefault<z.ZodNumber>;
199
+ maxFiles: z.ZodDefault<z.ZodNumber>;
200
+ maxFileBytes: z.ZodDefault<z.ZodNumber>;
201
+ maxOutputBytes: z.ZodDefault<z.ZodNumber>;
202
+ maxLogBytes: z.ZodDefault<z.ZodNumber>;
203
+ }, z.core.$strict>>;
204
+ data: z.ZodDefault<z.ZodObject<{
205
+ maxUploadBytes: z.ZodDefault<z.ZodNumber>;
206
+ maxRows: z.ZodDefault<z.ZodNumber>;
207
+ queryTimeoutMs: z.ZodDefault<z.ZodNumber>;
208
+ maxInteractiveRows: z.ZodDefault<z.ZodNumber>;
209
+ }, z.core.$strict>>;
210
+ retention: z.ZodDefault<z.ZodObject<{
211
+ jobsDays: z.ZodDefault<z.ZodNumber>;
212
+ intermediateDays: z.ZodDefault<z.ZodNumber>;
213
+ artifactsDays: z.ZodDefault<z.ZodNumber>;
214
+ }, z.core.$strict>>;
215
+ }, z.core.$strict>>;
177
216
  tui: z.ZodDefault<z.ZodObject<{
178
217
  paddingX: z.ZodDefault<z.ZodNumber>;
179
218
  contentPaddingX: z.ZodDefault<z.ZodNumber>;
@@ -235,6 +274,34 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
235
274
  maxOutputTokens: number;
236
275
  maxTokens?: number | undefined;
237
276
  };
277
+ analysis: {
278
+ enabled: boolean;
279
+ runtime: "managed" | "oci";
280
+ oci: {
281
+ engine: "docker" | "podman";
282
+ image?: string | undefined;
283
+ memoryMb: number;
284
+ cpus: number;
285
+ };
286
+ limits: {
287
+ timeoutMs: number;
288
+ maxFiles: number;
289
+ maxFileBytes: number;
290
+ maxOutputBytes: number;
291
+ maxLogBytes: number;
292
+ };
293
+ data: {
294
+ maxUploadBytes: number;
295
+ maxRows: number;
296
+ queryTimeoutMs: number;
297
+ maxInteractiveRows: number;
298
+ };
299
+ retention: {
300
+ jobsDays: number;
301
+ intermediateDays: number;
302
+ artifactsDays: number;
303
+ };
304
+ };
238
305
  tui: {
239
306
  paddingX: number;
240
307
  contentPaddingX: number;
@@ -379,6 +446,34 @@ export declare const configSchema: z.ZodPipe<z.ZodObject<{
379
446
  maxOutputTokens: number;
380
447
  maxTokens?: number | undefined;
381
448
  };
449
+ analysis: {
450
+ enabled: boolean;
451
+ runtime: "managed" | "oci";
452
+ oci: {
453
+ engine: "docker" | "podman";
454
+ image?: string | undefined;
455
+ memoryMb: number;
456
+ cpus: number;
457
+ };
458
+ limits: {
459
+ timeoutMs: number;
460
+ maxFiles: number;
461
+ maxFileBytes: number;
462
+ maxOutputBytes: number;
463
+ maxLogBytes: number;
464
+ };
465
+ data: {
466
+ maxUploadBytes: number;
467
+ maxRows: number;
468
+ queryTimeoutMs: number;
469
+ maxInteractiveRows: number;
470
+ };
471
+ retention: {
472
+ jobsDays: number;
473
+ intermediateDays: number;
474
+ artifactsDays: number;
475
+ };
476
+ };
382
477
  tui: {
383
478
  paddingX: number;
384
479
  contentPaddingX: number;
@@ -405,6 +500,8 @@ export interface ConfigProvenance {
405
500
  kind: "project" | "explicit";
406
501
  hasLegacyProvider: boolean;
407
502
  };
503
+ /** Keys a project or explicit layer set that only the global layer may decide (ignored). */
504
+ ignored?: string[];
408
505
  }
409
506
  export interface ConfigLoadResult {
410
507
  config: LoadedConfig;
@@ -473,6 +570,11 @@ declare const SETTABLE_KEYS: {
473
570
  }>>;
474
571
  readonly "agents.active": z.ZodDefault<z.ZodString>;
475
572
  readonly "agents.effort": z.ZodOptional<z.ZodString>;
573
+ readonly "analysis.enabled": z.ZodDefault<z.ZodBoolean>;
574
+ readonly "analysis.limits.timeoutMs": z.ZodDefault<z.ZodNumber>;
575
+ readonly "analysis.retention.jobsDays": z.ZodDefault<z.ZodNumber>;
576
+ readonly "analysis.retention.intermediateDays": z.ZodDefault<z.ZodNumber>;
577
+ readonly "analysis.retention.artifactsDays": z.ZodDefault<z.ZodNumber>;
476
578
  };
477
579
  export type SettableSettingKey = keyof typeof SETTABLE_KEYS;
478
580
  export declare function isSettableSettingKey(key: string): key is SettableSettingKey;
package/dist/config.js CHANGED
@@ -75,6 +75,13 @@ const serversSchema = (value) => z.record(z.string(), value).superRefine((server
75
75
  message: "MCP server names may contain only letters, numbers, dot, underscore and dash",
76
76
  });
77
77
  });
78
+ /**
79
+ * A container image reference pinned by digest. Anchored at the start with a letter or digit so a
80
+ * value can never be read as an option by the container CLI.
81
+ */
82
+ export const OCI_IMAGE_PATTERN = /^[a-zA-Z0-9][^\s@]*@sha256:[a-f0-9]{64}$/;
83
+ /** Analysis keys only the user (global) layer decides; a project layer cannot set them. */
84
+ const GLOBAL_ONLY_ANALYSIS = ["runtime", "oci", "retention"];
78
85
  const configObjectSchema = z
79
86
  .object({
80
87
  schemaVersion: z.literal(1).default(1),
@@ -182,6 +189,124 @@ const configObjectSchema = z
182
189
  maxContextChars: 800000,
183
190
  maxOutputTokens: 16384,
184
191
  })),
192
+ /**
193
+ * Python analysis and downloadable artifacts. `enabled: false` registers none of the
194
+ * analysis tools. The interpreter is discovered automatically (or pinned with `--python`).
195
+ */
196
+ analysis: z
197
+ .object({
198
+ enabled: z.boolean().default(true),
199
+ /**
200
+ * Where `python_run` executes. `managed` is the discovered Python (not a sandbox); `oci`
201
+ * is a Docker or Podman container started from a pinned image. GLOBAL ONLY: a project
202
+ * layer cannot choose the runtime or the image (it is ignored with a diagnostic).
203
+ */
204
+ runtime: z.enum(["managed", "oci"]).default("managed"),
205
+ /** Container runtime settings (global only). Used when `runtime` is `oci`. */
206
+ oci: z
207
+ .object({
208
+ engine: z.enum(["docker", "podman"]).default("docker"),
209
+ /** `name@sha256:<digest>`: an image without a digest is rejected at load time. */
210
+ image: z
211
+ .string()
212
+ .regex(OCI_IMAGE_PATTERN, "analysis.oci.image must be pinned by digest: <name>@sha256:<64 hex characters>")
213
+ .optional(),
214
+ memoryMb: z.number().int().min(256).max(65_536).default(2048),
215
+ cpus: z.number().min(0.5).max(64).default(2),
216
+ })
217
+ .strict()
218
+ .default(() => ({ engine: "docker", memoryMb: 2048, cpus: 2 })),
219
+ limits: z
220
+ .object({
221
+ /** Default and maximum run time of one python_run call. */
222
+ timeoutMs: z.number().int().min(1000).max(900_000).default(120_000),
223
+ /** Files published by one execution. */
224
+ maxFiles: z.number().int().min(1).max(1000).default(200),
225
+ maxFileBytes: z
226
+ .number()
227
+ .int()
228
+ .positive()
229
+ .default(100 * 1024 * 1024),
230
+ /** Total bytes published by one execution. */
231
+ maxOutputBytes: z
232
+ .number()
233
+ .int()
234
+ .positive()
235
+ .default(500 * 1024 * 1024),
236
+ /** Size cap of each stdout/stderr log of a job. */
237
+ maxLogBytes: z
238
+ .number()
239
+ .int()
240
+ .positive()
241
+ .default(10 * 1024 * 1024),
242
+ })
243
+ .strict()
244
+ .default(() => ({
245
+ timeoutMs: 120_000,
246
+ maxFiles: 200,
247
+ maxFileBytes: 100 * 1024 * 1024,
248
+ maxOutputBytes: 500 * 1024 * 1024,
249
+ maxLogBytes: 10 * 1024 * 1024,
250
+ })),
251
+ /** Tabular data (CSV, TSV, JSON, JSONL, XLSX) ingested into one SQLite file per dataset. */
252
+ data: z
253
+ .object({
254
+ /** Largest file ingested (an upload or a workspace file). */
255
+ maxUploadBytes: z
256
+ .number()
257
+ .int()
258
+ .positive()
259
+ .default(200 * 1024 * 1024),
260
+ /** Rows per sheet; a larger file stops the ingestion and leaves no dataset. */
261
+ maxRows: z.number().int().positive().default(5_000_000),
262
+ /** Time limit of one `data_query` statement; the engine process is killed past it. */
263
+ queryTimeoutMs: z.number().int().min(100).max(60_000).default(5000),
264
+ /** Sorting and filtering are disabled above this many rows (the file has no indexes). */
265
+ maxInteractiveRows: z.number().int().positive().default(1_000_000),
266
+ })
267
+ .strict()
268
+ .default(() => ({
269
+ maxUploadBytes: 200 * 1024 * 1024,
270
+ maxRows: 5_000_000,
271
+ queryTimeoutMs: 5000,
272
+ maxInteractiveRows: 1_000_000,
273
+ })),
274
+ /**
275
+ * Retention of analysis data (`0` disables the automatic deletion of that class). Global
276
+ * only: one sweep covers every workspace, so a project layer cannot shorten it.
277
+ */
278
+ retention: z
279
+ .object({
280
+ /** Logs and staging of a job; its script and inputs once no artifact is `ready`. */
281
+ jobsDays: z.number().int().min(0).max(3650).default(30),
282
+ /** The scratch folder (`work/`) of a job. */
283
+ intermediateDays: z.number().int().min(0).max(3650).default(7),
284
+ /** Artifacts older than this become `expired` (folder deleted, row kept). 0 = keep. */
285
+ artifactsDays: z.number().int().min(0).max(3650).default(0),
286
+ })
287
+ .strict()
288
+ .default(() => ({ jobsDays: 30, intermediateDays: 7, artifactsDays: 0 })),
289
+ })
290
+ .strict()
291
+ .default(() => ({
292
+ enabled: true,
293
+ runtime: "managed",
294
+ oci: { engine: "docker", memoryMb: 2048, cpus: 2 },
295
+ retention: { jobsDays: 30, intermediateDays: 7, artifactsDays: 0 },
296
+ limits: {
297
+ timeoutMs: 120_000,
298
+ maxFiles: 200,
299
+ maxFileBytes: 100 * 1024 * 1024,
300
+ maxOutputBytes: 500 * 1024 * 1024,
301
+ maxLogBytes: 10 * 1024 * 1024,
302
+ },
303
+ data: {
304
+ maxUploadBytes: 200 * 1024 * 1024,
305
+ maxRows: 5_000_000,
306
+ queryTimeoutMs: 5000,
307
+ maxInteractiveRows: 1_000_000,
308
+ },
309
+ })),
185
310
  tui: z
186
311
  .object({
187
312
  /** Horizontal padding (columns) around the editor input box. Editor-only. */
@@ -328,7 +453,11 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
328
453
  form: canonical && name in canonical ? "canonical" : "alias",
329
454
  },
330
455
  ]));
331
- return { config, keys: new Set(Object.keys(object)), sources };
456
+ const analysisRaw = object.analysis && typeof object.analysis === "object" && !Array.isArray(object.analysis)
457
+ ? object.analysis
458
+ : {};
459
+ const ignored = GLOBAL_ONLY_ANALYSIS.filter((key) => key in analysisRaw).map((key) => `analysis.${key}`);
460
+ return { config, keys: new Set(Object.keys(object)), sources, ignored, file };
332
461
  };
333
462
  const global = await parseLayer(globalFile, "global");
334
463
  // Skill activation is deliberately project-local; never carry a similarly named global field
@@ -339,8 +468,10 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
339
468
  // Parsed `provider.model` of the selected layer itself (before env/CLI overrides), used to
340
469
  // decide whether its legacy root `provider` is usable against a saved `/connect` profile.
341
470
  let selectedLegacyModel = "";
471
+ let ignoredKeys = [];
342
472
  if (selectedFile && selectedFile !== globalFile) {
343
473
  const selected = await parseLayer(selectedFile, options.file ? "explicit" : "project");
474
+ ignoredKeys = selected.ignored;
344
475
  selectedLegacyModel = selected.config.provider.model;
345
476
  const overlaid = { ...config };
346
477
  for (const key of selected.keys) {
@@ -383,6 +514,17 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
383
514
  overlaid.builtinPlugins = { ...overlaid.builtinPlugins, ...selected.config.builtinPlugins };
384
515
  continue;
385
516
  }
517
+ if (key === "analysis") {
518
+ // `runtime`, `oci` and `retention` are global-only (a repository must not pick the binary
519
+ // that runs scripts, nor shorten the retention of every workspace's data).
520
+ overlaid.analysis = {
521
+ ...selected.config.analysis,
522
+ runtime: global.config.analysis.runtime,
523
+ oci: global.config.analysis.oci,
524
+ retention: global.config.analysis.retention,
525
+ };
526
+ continue;
527
+ }
386
528
  overlaid[key] = selected.config[key];
387
529
  }
388
530
  if (selected.keys.has("mcp") || selected.keys.has("mcpServers"))
@@ -421,6 +563,7 @@ export async function loadConfigWithProvenance(workspace, options = {}) {
421
563
  },
422
564
  }
423
565
  : {}),
566
+ ...(ignoredKeys.length ? { ignored: ignoredKeys } : {}),
424
567
  },
425
568
  };
426
569
  }
@@ -528,7 +671,10 @@ const SETTABLE_SECTIONS = {
528
671
  tui: configObjectSchema.shape.tui.removeDefault(),
529
672
  websearch: configObjectSchema.shape.websearch.removeDefault(),
530
673
  agents: configObjectSchema.shape.agents.removeDefault(),
674
+ analysis: configObjectSchema.shape.analysis.removeDefault(),
531
675
  };
676
+ const ANALYSIS_LIMITS = SETTABLE_SECTIONS.analysis.shape.limits.removeDefault();
677
+ const ANALYSIS_RETENTION = SETTABLE_SECTIONS.analysis.shape.retention.removeDefault();
532
678
  const SETTABLE_KEYS = {
533
679
  "compaction.auto": SETTABLE_SECTIONS.compaction.shape.auto,
534
680
  "compaction.threshold": SETTABLE_SECTIONS.compaction.shape.threshold,
@@ -547,6 +693,11 @@ const SETTABLE_KEYS = {
547
693
  "websearch.provider": SETTABLE_SECTIONS.websearch.shape.provider,
548
694
  "agents.active": SETTABLE_SECTIONS.agents.shape.active,
549
695
  "agents.effort": SETTABLE_SECTIONS.agents.shape.effort,
696
+ "analysis.enabled": SETTABLE_SECTIONS.analysis.shape.enabled,
697
+ "analysis.limits.timeoutMs": ANALYSIS_LIMITS.shape.timeoutMs,
698
+ "analysis.retention.jobsDays": ANALYSIS_RETENTION.shape.jobsDays,
699
+ "analysis.retention.intermediateDays": ANALYSIS_RETENTION.shape.intermediateDays,
700
+ "analysis.retention.artifactsDays": ANALYSIS_RETENTION.shape.artifactsDays,
550
701
  };
551
702
  export function isSettableSettingKey(key) {
552
703
  return Object.prototype.hasOwnProperty.call(SETTABLE_KEYS, key);
@@ -603,11 +754,21 @@ export async function setConfigValue(input) {
603
754
  throw new Error("Global Alisio configuration must be a JSON object");
604
755
  raw = existing;
605
756
  }
606
- const [section, leaf] = input.key.split(".");
607
- const container = raw[section] && typeof raw[section] === "object" && !Array.isArray(raw[section])
608
- ? raw[section]
609
- : {};
610
- raw[section] = { ...container, [leaf]: parsed.data };
757
+ // Keys are `section.leaf` or `section.group.leaf`: every object on the path keeps its other fields.
758
+ const path = input.key.split(".");
759
+ const setPath = (target, depth) => {
760
+ const name = path[depth];
761
+ if (depth === path.length - 1)
762
+ return { ...target, [name]: parsed.data };
763
+ const child = target[name];
764
+ return {
765
+ ...target,
766
+ [name]: setPath(child && typeof child === "object" && !Array.isArray(child)
767
+ ? child
768
+ : {}, depth + 1),
769
+ };
770
+ };
771
+ raw = setPath(raw, 0);
611
772
  raw.schemaVersion ??= 1;
612
773
  await mkdir(dirname(file), { recursive: true, mode: 0o700 });
613
774
  const temporary = `${file}.${process.pid}.${crypto.randomUUID()}.tmp`;
@@ -1,4 +1,4 @@
1
- import type { CompactionCheckpoint, Message, ToolCall, ToolResult } from "@alisio/sdk";
1
+ import type { AnalysisCapability, ArtifactPublisher, ArtifactRef, CompactionCheckpoint, InstallPreview, Message, ToolCall, ToolResult } from "@alisio/sdk";
2
2
  export interface Session {
3
3
  id: string;
4
4
  workspace: string;
@@ -188,6 +188,11 @@ export interface Policy {
188
188
  write: boolean;
189
189
  process: boolean;
190
190
  external: boolean;
191
+ /**
192
+ * Pre-allows the `analysis.run` capability only (`--allow-analysis`); never widens `process`.
193
+ * Absent means false.
194
+ */
195
+ analysis?: boolean;
191
196
  }
192
197
  export type ApprovalDecision = "once" | "session" | "deny";
193
198
  export interface ApprovalRequest {
@@ -199,7 +204,70 @@ export interface ApprovalRequest {
199
204
  effect: "write" | "process" | "external";
200
205
  input: Record<string, unknown>;
201
206
  signal: AbortSignal;
207
+ /** The approval is for this capability of the tool, not for the whole effect. */
208
+ capability?: AnalysisCapability;
209
+ /** Capability approvals: the first 40 lines of the script. */
210
+ preview?: string;
211
+ /** Capability approvals: how the code runs (`managed` is not sandboxed). */
212
+ runtime?: "managed" | "oci";
213
+ /** `analysis.install`: the packages, the size estimate and the network need (once or deny). */
214
+ install?: InstallPreview;
202
215
  }
216
+ /**
217
+ * Persisted capability grants as the runner sees them (implemented by `CapabilityGrants`). A
218
+ * `session` decision is stored for the ROOT session and survives restarts; `once` and `deny`
219
+ * decisions are audit rows only.
220
+ */
221
+ export interface CapabilityGate {
222
+ granted(capability: AnalysisCapability, sessionId: string): boolean;
223
+ record(input: {
224
+ capability: AnalysisCapability;
225
+ sessionId: string;
226
+ scope: "once" | "session";
227
+ decision: "allow" | "deny";
228
+ source: "tui" | "web" | "flag" | "headless-grant";
229
+ callId?: string;
230
+ runId?: string;
231
+ correlationId?: string;
232
+ }): void;
233
+ /** Recorded as the source of interactive decisions (`tui` or `web`). */
234
+ source?: "tui" | "web";
235
+ /** Runtime mode shown with `analysis.run` approvals (read live: the configuration can change). */
236
+ runtime?: "managed" | "oci";
237
+ /**
238
+ * The script to show in an approval whose input does not carry it (a rerun runs a saved script).
239
+ * Absent or undefined: the preview is taken from `input.code`.
240
+ */
241
+ preview?(input: Record<string, unknown>, sessionId: string): Promise<string | undefined>;
242
+ }
243
+ /** One artifact published during a tool call, with its local path (for `artifact_published`). */
244
+ export interface PublishedArtifactInfo {
245
+ artifact: ArtifactRef;
246
+ path: string;
247
+ warnings: string[];
248
+ executionId?: string;
249
+ }
250
+ /** The publisher core tools receive; `publishOutputs` publishes a whole staging folder at once. */
251
+ export interface CoreArtifactPublisher extends ArtifactPublisher {
252
+ publishOutputs(staging: string, options: {
253
+ executionId: string;
254
+ title?: string;
255
+ partial?: boolean;
256
+ provenance?: Record<string, unknown>;
257
+ }): Promise<PublishedArtifactInfo[]>;
258
+ /** Detailed variant of `publishText` (path and warnings). */
259
+ publishTextDetailed(input: {
260
+ fileName: string;
261
+ title?: string;
262
+ text: string;
263
+ }): Promise<PublishedArtifactInfo>;
264
+ }
265
+ /** Creates the publisher of one tool call; `announce` emits `artifact_published`. */
266
+ export type ArtifactPublisherFactory = (call: {
267
+ sessionId: string;
268
+ runId: string;
269
+ callId: string;
270
+ }, announce: (published: PublishedArtifactInfo) => void) => CoreArtifactPublisher | undefined;
203
271
  /** Optional interactive approval for write/process/external tools not pre-allowed by the Policy. */
204
272
  export type ApprovalHandler = (request: ApprovalRequest) => Promise<ApprovalDecision>;
205
273
  export interface HookFailure {
@@ -1,6 +1,6 @@
1
- import { type Attachment, type Message, type ModelProvider, type RunEvent, type ToolDefinition } from "@alisio/sdk";
1
+ import { type Attachment, type DatasetRef, type Message, type ModelProvider, type RunEvent, type ToolDefinition, type ToolResult } from "@alisio/sdk";
2
2
  import type { PathAccess } from "../runtime/access.ts";
3
- import type { ApprovalHandler, ContextSource, Policy, RunnerExtensions, SessionStore } from "./contracts.ts";
3
+ import type { ApprovalHandler, ArtifactPublisherFactory, CapabilityGate, ContextSource, Policy, RunnerExtensions, SessionStore } from "./contracts.ts";
4
4
  import type { ToolRegistry } from "./registry.ts";
5
5
  export interface CompactionSettings {
6
6
  /** Compact automatically when context usage crosses `threshold` of a known window. */
@@ -42,6 +42,10 @@ export interface RunnerOptions {
42
42
  pathAccess?: PathAccess;
43
43
  /** Generic hooks (implemented by the plugin host) for compaction and session start. */
44
44
  extensions?: RunnerExtensions;
45
+ /** Persisted capability grants (`analysis.run`); without it capabilities only follow `effect`. */
46
+ capabilities?: CapabilityGate;
47
+ /** Artifact publisher of each tool call (`ToolContext.artifacts`). */
48
+ artifacts?: ArtifactPublisherFactory;
45
49
  /**
46
50
  * Provider-native tool definitions (for example a hosted `web_search` tool) appended to every
47
51
  * request's `tools` alongside the registry's function tools. Opt-in, set once for the whole
@@ -76,6 +80,8 @@ export interface RunOptions {
76
80
  display?: string;
77
81
  /** Images attached to this turn's user message, sent as vision content parts. */
78
82
  attachments?: Attachment[];
83
+ /** Datasets attached to this turn (UI chips); their text summary is part of the prompt. */
84
+ datasets?: DatasetRef[];
79
85
  /** Extra system instructions for this run (e.g. an agent persona). */
80
86
  instructions?: string;
81
87
  toolFilter?: (tool: ToolDefinition) => boolean;
@@ -130,8 +136,11 @@ export declare class AgentRunner {
130
136
  */
131
137
  applySettings(patch: RunnerSettingsPatch): void;
132
138
  private emitter;
133
- /** Tools offered to the model under the current policy (and optional run narrowing). */
134
- availableTools(run?: RunOptions): ToolDefinition[];
139
+ /**
140
+ * Tools offered to the model under the current policy (and optional run narrowing). With a
141
+ * session, a capability tool is also offered when that session's root holds a persisted grant.
142
+ */
143
+ availableTools(run?: RunOptions, sessionId?: string): ToolDefinition[];
135
144
  /** Abort signal of the session's active run, if any (used to cascade cancellation). */
136
145
  signal(sessionId: string): AbortSignal | undefined;
137
146
  isRunning(sessionId: string): boolean;
@@ -182,6 +191,27 @@ export declare class AgentRunner {
182
191
  signal?: AbortSignal;
183
192
  }): Promise<CompactionResult | undefined>;
184
193
  private compactLocked;
194
+ /**
195
+ * Gates and executes one tool call: external-directory paths, the capability/effect gate with
196
+ * its approval, the call journal, artifact publishing and the result bound. Shared by model
197
+ * runs and `runToolCall`, so a call started from a UI passes exactly the same gates.
198
+ */
199
+ private executeCall;
200
+ /**
201
+ * Runs ONE tool call without a model (spec §20 `runToolCall`): an action the user started from
202
+ * a UI, such as copying an artifact into the workspace. It is a real run of the session
203
+ * (`run_started` → `tool_*` → `run_completed`, journaled) and the call passes exactly the gates
204
+ * of a model call (`write` approval, capabilities, paths). The transcript stays valid for every
205
+ * provider: a user note (shown as `display`), an assistant message with the call, the tool
206
+ * result and a short assistant summary.
207
+ */
208
+ runToolCall(sessionId: string, name: string, input: Record<string, unknown>, options?: RunOptions & {
209
+ signal?: AbortSignal;
210
+ }): Promise<{
211
+ runId: string;
212
+ status: "completed";
213
+ result: ToolResult;
214
+ }>;
185
215
  run(sessionId: string, prompt: string, signal?: AbortSignal, options?: RunOptions): Promise<{
186
216
  sessionId: string;
187
217
  text: string;