@alisio/core 0.1.0-alpha.21 → 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 (89) 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 +195 -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 +102 -0
  59. package/dist/config.js +167 -6
  60. package/dist/core/contracts.d.ts +69 -1
  61. package/dist/core/runner.d.ts +34 -4
  62. package/dist/core/runner.js +388 -130
  63. package/dist/index.d.ts +23 -4
  64. package/dist/index.js +22 -3
  65. package/dist/mcp/rich.js +4 -0
  66. package/dist/plugins/host.js +14 -6
  67. package/dist/runtime/blobs.d.ts +22 -0
  68. package/dist/runtime/blobs.js +84 -1
  69. package/dist/runtime/ids.d.ts +8 -0
  70. package/dist/runtime/ids.js +32 -0
  71. package/dist/runtime/paths.d.ts +5 -0
  72. package/dist/runtime/paths.js +6 -0
  73. package/dist/runtime/process.d.ts +12 -0
  74. package/dist/runtime/process.js +64 -4
  75. package/dist/runtime/sqlite.d.ts +2 -0
  76. package/dist/runtime/sqlite.js +18 -0
  77. package/dist/runtime/store.d.ts +5 -0
  78. package/dist/runtime/store.js +75 -0
  79. package/dist/runtime/zip.d.ts +24 -0
  80. package/dist/runtime/zip.js +126 -0
  81. package/dist/sessions/children.js +4 -0
  82. package/dist/tools/analysis.d.ts +37 -0
  83. package/dist/tools/analysis.js +572 -0
  84. package/dist/tools/artifacts.d.ts +14 -0
  85. package/dist/tools/artifacts.js +143 -0
  86. package/dist/tools/data.d.ts +7 -0
  87. package/dist/tools/data.js +160 -0
  88. package/dist/tools/execute.js +6 -2
  89. package/package.json +2 -2
@@ -17,7 +17,19 @@ const MAX_INJECT_CHARS = 24_000;
17
17
  * fails with an actionable error instead of silently losing the whole history.
18
18
  */
19
19
  const MIN_MESSAGE_BUDGET_FLOOR = 4_000;
20
- const allowed = (policy, effect) => effect === "read" || effect === "internal" || !!policy[effect];
20
+ const allowed = (policy, effect) => effect === "read" ||
21
+ effect === "internal" ||
22
+ ((effect === "write" || effect === "process" || effect === "external") && !!policy[effect]);
23
+ /** A tool's capability is pre-allowed by the policy alone (`--allow-analysis`). */
24
+ const capabilityAllowed = (policy, tool) => tool.capability === "analysis.run" && !!policy.analysis;
25
+ /** First 40 lines of a script input, for capability approvals. */
26
+ const scriptPreview = (input) => typeof input.code === "string" ? input.code.split(/\r?\n/).slice(0, 40).join("\n") : undefined;
27
+ /** A short id for a tool call the user started from a UI (alphanumeric, 9 characters). */
28
+ const uiCallId = () => {
29
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
30
+ const bytes = crypto.getRandomValues(new Uint8Array(9));
31
+ return Array.from(bytes, (b) => alphabet[b % alphabet.length]).join("");
32
+ };
21
33
  export class AgentRunner {
22
34
  options;
23
35
  /** Sessions with an active run or compaction (one at a time per session). */
@@ -80,8 +92,11 @@ export class AgentRunner {
80
92
  }
81
93
  };
82
94
  }
83
- /** Tools offered to the model under the current policy (and optional run narrowing). */
84
- availableTools(run = {}) {
95
+ /**
96
+ * Tools offered to the model under the current policy (and optional run narrowing). With a
97
+ * session, a capability tool is also offered when that session's root holds a persisted grant.
98
+ */
99
+ availableTools(run = {}, sessionId) {
85
100
  const o = this.options;
86
101
  const policy = run.policy ?? o.policy;
87
102
  const approvals = !!o.approve && run.approvals !== false;
@@ -91,6 +106,12 @@ export class AgentRunner {
91
106
  const effect = t.effect ?? "external";
92
107
  if (allowed(policy, effect))
93
108
  return true;
109
+ if (t.capability) {
110
+ if (capabilityAllowed(policy, t))
111
+ return true;
112
+ if (sessionId && o.capabilities?.granted(t.capability, sessionId))
113
+ return true;
114
+ }
94
115
  return approvals && (effect === "write" || effect === "process" || effect === "external");
95
116
  });
96
117
  }
@@ -310,6 +331,343 @@ export class AgentRunner {
310
331
  throw error;
311
332
  }
312
333
  }
334
+ /**
335
+ * Gates and executes one tool call: external-directory paths, the capability/effect gate with
336
+ * its approval, the call journal, artifact publishing and the result bound. Shared by model
337
+ * runs and `runToolCall`, so a call started from a UI passes exactly the same gates.
338
+ */
339
+ async executeCall(scope, p) {
340
+ const o = this.options;
341
+ const { sessionId, runId, emit, workspace, policy, approvals, options } = scope;
342
+ const combined = scope.signal;
343
+ const pathAccess = o.pathAccess;
344
+ const { call } = p;
345
+ let result;
346
+ const started = Date.now();
347
+ // `effect` rides along so the TUI can group consecutive calls by capability kind
348
+ // (read batches, write batches, commands…) without a name heuristic. Additive field.
349
+ emit("tool_started", {
350
+ name: call.name,
351
+ id: call.id,
352
+ arguments: call.arguments,
353
+ effect: p.tool?.effect ?? "external",
354
+ });
355
+ try {
356
+ combined.throwIfAborted();
357
+ if (p.error)
358
+ throw new Error(p.error);
359
+ if (!p.tool || !p.input)
360
+ throw new Error("Invalid tool");
361
+ const effect = p.tool.effect ?? "external";
362
+ // External-directory prerequisite: declared paths are resolved (and, outside every
363
+ // allowed root, approved for the containing directory) BEFORE the effect gate, so a
364
+ // write still needs its own write policy and a read never hard-throws first. The
365
+ // resolved map keeps "allow once" scoped to this one call, with no shared mutable
366
+ // state that parallel read batches could race on.
367
+ const resolved = new Map();
368
+ if (pathAccess)
369
+ for (const declared of p.tool.paths?.(p.input) ?? [])
370
+ if (!resolved.has(declared)) {
371
+ const target = await pathAccess.resolve(declared, {
372
+ workspace,
373
+ session: sessionId,
374
+ ...(options.label ? { label: options.label } : {}),
375
+ signal: combined,
376
+ });
377
+ // Key both the declared input and its resolved form: a write resolves the
378
+ // input once, then re-checks the already-absolute path, and neither lookup may
379
+ // ask a second time for an "allow once" decision.
380
+ resolved.set(declared, target);
381
+ resolved.set(target, target);
382
+ }
383
+ scope.guard?.(effect);
384
+ if (options.toolFilter && !options.toolFilter(p.tool))
385
+ throw new Error(`Tool ${call.name} is not available in this session`);
386
+ const capability = p.tool.capability;
387
+ const gate = o.capabilities;
388
+ if (capability && (allowed(policy, effect) || capabilityAllowed(policy, p.tool))) {
389
+ // A broad process grant or --allow-analysis: allowed, audited as a flag decision.
390
+ gate?.record({
391
+ capability,
392
+ sessionId,
393
+ scope: "once",
394
+ decision: "allow",
395
+ source: "flag",
396
+ callId: call.id,
397
+ runId,
398
+ ...(options.correlationId !== undefined ? { correlationId: options.correlationId } : {}),
399
+ });
400
+ }
401
+ else if (capability && gate?.granted(capability, sessionId)) {
402
+ // A persisted "allow for this session" grant of the root session.
403
+ }
404
+ else if (!allowed(policy, effect)) {
405
+ if (!o.approve ||
406
+ !approvals ||
407
+ (effect !== "write" && effect !== "process" && effect !== "external"))
408
+ throw new Error(`Capability denied: ${capability ?? effect}`);
409
+ const preview = capability
410
+ ? (scriptPreview(p.input) ?? (await gate?.preview?.(p.input, sessionId)))
411
+ : undefined;
412
+ emit("approval_requested", {
413
+ id: call.id,
414
+ name: call.name,
415
+ effect,
416
+ ...(options.label ? { label: options.label } : {}),
417
+ ...(capability ? { capability } : {}),
418
+ });
419
+ const decision = await o.approve({
420
+ call,
421
+ effect,
422
+ input: p.input,
423
+ signal: combined,
424
+ session: sessionId,
425
+ ...(options.label ? { label: options.label } : {}),
426
+ ...(capability ? { capability } : {}),
427
+ ...(preview !== undefined ? { preview } : {}),
428
+ ...(capability && gate?.runtime ? { runtime: gate.runtime } : {}),
429
+ });
430
+ combined.throwIfAborted();
431
+ const persisted = !!capability && !!gate && decision === "session";
432
+ if (capability && gate)
433
+ gate.record({
434
+ capability,
435
+ sessionId,
436
+ scope: decision === "session" ? "session" : "once",
437
+ decision: decision === "deny" ? "deny" : "allow",
438
+ source: gate.source ?? "tui",
439
+ callId: call.id,
440
+ runId,
441
+ ...(options.correlationId !== undefined
442
+ ? { correlationId: options.correlationId }
443
+ : {}),
444
+ });
445
+ emit("approval_resolved", {
446
+ id: call.id,
447
+ name: call.name,
448
+ effect,
449
+ decision,
450
+ ...(capability ? { capability } : {}),
451
+ ...(persisted ? { persisted: true } : {}),
452
+ });
453
+ if (decision === "deny")
454
+ throw new Error(`Capability ${capability ?? effect} denied by the user for this call`);
455
+ // A capability grant never widens the effect: only effect approvals do.
456
+ if (decision === "session" && !capability)
457
+ policy[effect] = true;
458
+ }
459
+ o.store.beginCall(sessionId, call, { runId, effect });
460
+ const publisher = o.artifacts?.({ sessionId, runId, callId: call.id }, (published) => emit("artifact_published", {
461
+ artifact: published.artifact,
462
+ path: published.path,
463
+ callId: call.id,
464
+ ...(published.executionId ? { executionId: published.executionId } : {}),
465
+ }));
466
+ /**
467
+ * `analysis.install`: always asks, `once` only, and no flag or earlier grant covers it. Where
468
+ * nobody can be asked (headless, no approval handler) the answer is `deny`.
469
+ */
470
+ const approveInstall = async (install) => {
471
+ if (!o.approve || !approvals)
472
+ return "deny";
473
+ const installCall = { ...call, id: `${call.id}:install` };
474
+ emit("approval_requested", {
475
+ id: installCall.id,
476
+ name: call.name,
477
+ effect: "process",
478
+ ...(options.label ? { label: options.label } : {}),
479
+ capability: "analysis.install",
480
+ install,
481
+ });
482
+ let decision = "deny";
483
+ try {
484
+ decision = await o.approve({
485
+ call: installCall,
486
+ effect: "process",
487
+ input: { extras: install.extras },
488
+ signal: combined,
489
+ session: sessionId,
490
+ ...(options.label ? { label: options.label } : {}),
491
+ capability: "analysis.install",
492
+ install,
493
+ });
494
+ }
495
+ finally {
496
+ combined.throwIfAborted();
497
+ }
498
+ // A `session` answer is treated as `once`: an installation permission is never stored.
499
+ const allowedNow = decision !== "deny";
500
+ gate?.record({
501
+ capability: "analysis.install",
502
+ sessionId,
503
+ scope: "once",
504
+ decision: allowedNow ? "allow" : "deny",
505
+ source: gate.source ?? "tui",
506
+ callId: call.id,
507
+ runId,
508
+ ...(options.correlationId !== undefined ? { correlationId: options.correlationId } : {}),
509
+ });
510
+ emit("approval_resolved", {
511
+ id: installCall.id,
512
+ name: call.name,
513
+ effect: "process",
514
+ decision: allowedNow ? "once" : "deny",
515
+ capability: "analysis.install",
516
+ });
517
+ return allowedNow ? "once" : "deny";
518
+ };
519
+ result = await p.tool.execute(p.input, {
520
+ signal: combined,
521
+ workspace,
522
+ session: sessionId,
523
+ runId,
524
+ callId: call.id,
525
+ ...(publisher ? { artifacts: publisher } : {}),
526
+ approveInstall,
527
+ emit: (data) => emit("tool_progress", { id: call.id, data }),
528
+ ...(options.label ? { label: options.label } : {}),
529
+ ...(pathAccess
530
+ ? {
531
+ resolvePath: (path) => {
532
+ const known = resolved.get(path);
533
+ return known !== undefined
534
+ ? Promise.resolve(known)
535
+ : pathAccess.resolve(path, {
536
+ workspace,
537
+ session: sessionId,
538
+ ...(options.label ? { label: options.label } : {}),
539
+ signal: combined,
540
+ });
541
+ },
542
+ }
543
+ : {}),
544
+ });
545
+ // The bound applies to what the model sees (the text projection): display-only
546
+ // ui/image parts are bounded by their producers and must not push text over it.
547
+ const projected = textProjection(result);
548
+ if (JSON.stringify(projected).length > 48_000)
549
+ result = textResult(`${JSON.stringify(projected).slice(0, 40_000)}\n[tool output truncated]`, result.isError);
550
+ result = scope.amend?.(result, effect) ?? result;
551
+ o.store.endCall(sessionId, call, result);
552
+ }
553
+ catch (error) {
554
+ result = textResult(error instanceof Error ? error.message : String(error), true);
555
+ // A cancellation after execution may have left effects; journal remains pending.
556
+ if (!combined.aborted)
557
+ o.store.endCall(sessionId, call, result);
558
+ }
559
+ emit("tool_completed", {
560
+ id: call.id,
561
+ name: call.name,
562
+ isError: result.isError ?? false,
563
+ durationMs: Date.now() - started,
564
+ preview: result.content
565
+ .filter((part) => part.type === "text")
566
+ .map((c) => c.text)
567
+ .join("\n")
568
+ .slice(0, 2_000),
569
+ });
570
+ return result;
571
+ }
572
+ /**
573
+ * Runs ONE tool call without a model (spec §20 `runToolCall`): an action the user started from
574
+ * a UI, such as copying an artifact into the workspace. It is a real run of the session
575
+ * (`run_started` → `tool_*` → `run_completed`, journaled) and the call passes exactly the gates
576
+ * of a model call (`write` approval, capabilities, paths). The transcript stays valid for every
577
+ * provider: a user note (shown as `display`), an assistant message with the call, the tool
578
+ * result and a short assistant summary.
579
+ */
580
+ async runToolCall(sessionId, name, input, options = {}) {
581
+ const controller = this.claim(sessionId);
582
+ const runId = options.runId ?? crypto.randomUUID();
583
+ const o = this.options, emit = this.emitter(sessionId, { runId, correlationId: options.correlationId });
584
+ const finish = (status, error) => {
585
+ try {
586
+ o.store.endRun?.(runId, {
587
+ status,
588
+ usage: { input: 0, output: 0 },
589
+ ...(error ? { error } : {}),
590
+ });
591
+ }
592
+ catch {
593
+ /* A journal write failure cannot invalidate an executed run. */
594
+ }
595
+ };
596
+ const timeout = AbortSignal.timeout(options.timeoutMs ?? o.timeoutMs ?? 300_000);
597
+ const combined = AbortSignal.any([
598
+ controller.signal,
599
+ timeout,
600
+ ...(options.signal ? [options.signal] : []),
601
+ ]);
602
+ const workspace = options.workspace ?? o.workspace;
603
+ let acquired = false;
604
+ try {
605
+ o.store.beginRun?.({
606
+ id: runId,
607
+ session: sessionId,
608
+ status: "running",
609
+ model: o.store.get(sessionId).model,
610
+ ...(options.correlationId !== undefined ? { correlationId: options.correlationId } : {}),
611
+ });
612
+ o.store.acquire(sessionId);
613
+ acquired = true;
614
+ o.store.reconcile(sessionId);
615
+ const session = o.store.get(sessionId);
616
+ if (session.workspace !== workspace)
617
+ throw new Error("Session workspace mismatch");
618
+ combined.throwIfAborted();
619
+ const call = { id: uiCallId(), name, arguments: JSON.stringify(input) };
620
+ let prepared;
621
+ try {
622
+ prepared = {
623
+ call,
624
+ tool: o.registry.get(name),
625
+ input: o.registry.parse(name, call.arguments),
626
+ };
627
+ }
628
+ catch (error) {
629
+ prepared = { call, error: String(error) };
630
+ }
631
+ const note = `The user ran the ${name} tool from the interface.`;
632
+ o.store.append(sessionId, { role: "user", text: note, display: options.display ?? note });
633
+ emit("run_started", { model: session.model });
634
+ o.store.append(sessionId, { role: "assistant", text: "", calls: [call] });
635
+ const result = await this.executeCall({
636
+ sessionId,
637
+ runId,
638
+ emit,
639
+ signal: combined,
640
+ workspace,
641
+ policy: options.policy ?? o.policy,
642
+ approvals: !!o.approve && options.approvals !== false,
643
+ options,
644
+ }, prepared);
645
+ o.store.append(sessionId, { role: "tool", callId: call.id, result });
646
+ const first = textProjection(result)
647
+ .content.map((part) => (part.type === "text" ? part.text : ""))
648
+ .join(" ")
649
+ .replace(/\s*\n\s*/g, " ")
650
+ .trim()
651
+ .slice(0, 300);
652
+ const text = `${name} ${result.isError ? "failed" : "finished"}${first ? `: ${first}` : ""}`;
653
+ o.store.append(sessionId, { role: "assistant", text, calls: [] });
654
+ emit("run_completed", { tokens: 0, text });
655
+ finish("completed");
656
+ return { runId, status: "completed", result };
657
+ }
658
+ catch (error) {
659
+ const cause = combined.aborted ? (combined.reason ?? error) : error;
660
+ const message = cause instanceof Error ? cause.message : String(cause);
661
+ emit(combined.aborted ? "run_cancelled" : "run_failed", { error: message });
662
+ finish(combined.aborted ? "cancelled" : "failed", message);
663
+ throw error;
664
+ }
665
+ finally {
666
+ if (acquired)
667
+ o.store.release(sessionId);
668
+ this.active.delete(sessionId);
669
+ }
670
+ }
313
671
  async run(sessionId, prompt, signal, options = {}) {
314
672
  const controller = this.claim(sessionId);
315
673
  const runId = options.runId ?? crypto.randomUUID();
@@ -394,6 +752,7 @@ export class AgentRunner {
394
752
  text: prompt,
395
753
  ...(options.display ? { display: options.display } : {}),
396
754
  ...(options.attachments?.length ? { attachments: options.attachments } : {}),
755
+ ...(options.datasets?.length ? { datasets: options.datasets } : {}),
397
756
  });
398
757
  emit("run_started", { model });
399
758
  const budget = options.maxTokens ?? o.maxTokens ?? defaultTokenBudget(o.contextWindow?.(model));
@@ -407,7 +766,7 @@ export class AgentRunner {
407
766
  o.store.append(sessionId, { role: "user", text: queued });
408
767
  let instructions = await withPersona();
409
768
  let messages = o.store.messages(sessionId);
410
- const tools = this.availableTools(options);
769
+ const tools = this.availableTools(options, sessionId);
411
770
  const toolsText = this.toolsText(tools);
412
771
  const chars = () => instructions.length + JSON.stringify(messages).length + toolsText.length;
413
772
  // REDUCIBLE content only (instructions + transcript). The fixed tool catalog
@@ -556,137 +915,36 @@ export class AgentRunner {
556
915
  if (paths.length)
557
916
  contextUpdate = await context.beforePaths(paths, sessionId);
558
917
  let pendingUpdate = contextUpdate;
559
- const execute = async (p) => {
560
- const { call } = p;
561
- let result;
562
- const started = Date.now();
563
- // `effect` rides along so the TUI can group consecutive calls by capability kind
564
- // (read batches, write batches, commands…) without a name heuristic. Additive field.
565
- emit("tool_started", {
566
- name: call.name,
567
- id: call.id,
568
- arguments: call.arguments,
569
- effect: p.tool?.effect ?? "external",
570
- });
571
- try {
572
- combined.throwIfAborted();
573
- if (p.error)
574
- throw new Error(p.error);
575
- if (!p.tool || !p.input)
576
- throw new Error("Invalid tool");
577
- const effect = p.tool.effect ?? "external";
578
- // External-directory prerequisite: declared paths are resolved (and, outside every
579
- // allowed root, approved for the containing directory) BEFORE the effect gate, so a
580
- // write still needs its own write policy and a read never hard-throws first. The
581
- // resolved map keeps "allow once" scoped to this one call, with no shared mutable
582
- // state that parallel read batches could race on.
583
- const resolved = new Map();
584
- if (pathAccess)
585
- for (const declared of p.tool.paths?.(p.input) ?? [])
586
- if (!resolved.has(declared)) {
587
- const target = await pathAccess.resolve(declared, {
588
- workspace,
589
- session: sessionId,
590
- ...(options.label ? { label: options.label } : {}),
591
- signal: combined,
592
- });
593
- // Key both the declared input and its resolved form: a write resolves the
594
- // input once, then re-checks the already-absolute path, and neither lookup may
595
- // ask a second time for an "allow once" decision.
596
- resolved.set(declared, target);
597
- resolved.set(target, target);
598
- }
918
+ const scope = {
919
+ sessionId,
920
+ runId,
921
+ emit,
922
+ signal: combined,
923
+ workspace,
924
+ policy,
925
+ approvals,
926
+ options,
927
+ guard: (effect) => {
599
928
  if (contextUpdate && effect !== "read")
600
929
  throw new Error(`Context changed; reconsider this call before retrying.\n${contextUpdate}`);
601
930
  if (tokens >= budget)
602
931
  throw new Error("Token budget exhausted; tool was not executed");
603
- if (options.toolFilter && !options.toolFilter(p.tool))
604
- throw new Error(`Tool ${call.name} is not available in this session`);
605
- if (!allowed(policy, effect)) {
606
- if (!o.approve ||
607
- !approvals ||
608
- (effect !== "write" && effect !== "process" && effect !== "external"))
609
- throw new Error(`Capability denied: ${effect}`);
610
- emit("approval_requested", {
611
- id: call.id,
612
- name: call.name,
613
- effect,
614
- ...(options.label ? { label: options.label } : {}),
615
- });
616
- const decision = await o.approve({
617
- call,
618
- effect,
619
- input: p.input,
620
- signal: combined,
621
- session: sessionId,
622
- ...(options.label ? { label: options.label } : {}),
623
- });
624
- combined.throwIfAborted();
625
- emit("approval_resolved", { id: call.id, name: call.name, effect, decision });
626
- if (decision === "deny")
627
- throw new Error(`Capability ${effect} denied by the user for this call`);
628
- if (decision === "session")
629
- policy[effect] = true;
630
- }
631
- o.store.beginCall(sessionId, call, { runId, effect });
632
- result = await p.tool.execute(p.input, {
633
- signal: combined,
634
- workspace,
635
- session: sessionId,
636
- emit: (data) => emit("tool_progress", { id: call.id, data }),
637
- ...(options.label ? { label: options.label } : {}),
638
- ...(pathAccess
639
- ? {
640
- resolvePath: (path) => {
641
- const known = resolved.get(path);
642
- return known !== undefined
643
- ? Promise.resolve(known)
644
- : pathAccess.resolve(path, {
645
- workspace,
646
- session: sessionId,
647
- ...(options.label ? { label: options.label } : {}),
648
- signal: combined,
649
- });
650
- },
651
- }
652
- : {}),
653
- });
654
- // The bound applies to what the model sees (the text projection): display-only
655
- // ui/image parts are bounded by their producers and must not push text over it.
656
- const projected = textProjection(result);
657
- if (JSON.stringify(projected).length > 48_000)
658
- result = textResult(`${JSON.stringify(projected).slice(0, 40_000)}\n[tool output truncated]`, result.isError);
659
- if (pendingUpdate && effect === "read") {
660
- result = {
661
- ...result,
662
- content: [
663
- ...result.content,
664
- { type: "text", text: `\n<instructions>\n${pendingUpdate}\n</instructions>` },
665
- ],
666
- };
667
- pendingUpdate = undefined;
668
- }
669
- o.store.endCall(sessionId, call, result);
670
- }
671
- catch (error) {
672
- result = textResult(error instanceof Error ? error.message : String(error), true);
673
- // A cancellation after execution may have left effects; journal remains pending.
674
- if (!combined.aborted)
675
- o.store.endCall(sessionId, call, result);
676
- }
677
- emit("tool_completed", {
678
- id: call.id,
679
- name: call.name,
680
- isError: result.isError ?? false,
681
- durationMs: Date.now() - started,
682
- preview: result.content
683
- .filter((part) => part.type === "text")
684
- .map((c) => c.text)
685
- .join("\n")
686
- .slice(0, 2_000),
687
- });
688
- return result;
932
+ },
933
+ amend: (result, effect) => {
934
+ if (!pendingUpdate || effect !== "read")
935
+ return result;
936
+ const update = pendingUpdate;
937
+ pendingUpdate = undefined;
938
+ return {
939
+ ...result,
940
+ content: [
941
+ ...result.content,
942
+ { type: "text", text: `\n<instructions>\n${update}\n</instructions>` },
943
+ ],
944
+ };
945
+ },
689
946
  };
947
+ const execute = (p) => this.executeCall(scope, p);
690
948
  const results = [];
691
949
  // Bounded parallel batches for consecutive read or explicitly concurrent tools.
692
950
  const parallel = (p) => !!p?.tool && (p.tool.effect === "read" || p.tool.concurrent === true);
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";