@kal-elsam/kairo-runtime 0.2.1 → 0.2.3

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 (37) hide show
  1. package/README.md +13 -2
  2. package/package.json +1 -1
  3. package/scripts/runtime-mvp-smoke.sh +152 -0
  4. package/src/cli.js +97 -9
  5. package/src/global/brand/index.js +10 -0
  6. package/src/global/dashboard-guidance.js +66 -0
  7. package/src/global/initial-experience.js +34 -0
  8. package/src/global/ink/orchestrator-app.js +372 -82
  9. package/src/global/ink/orchestrator-state.js +196 -65
  10. package/src/global/ink/run-orchestrator-ink.js +2 -0
  11. package/src/global/ink/run-setup-ink.js +2 -0
  12. package/src/global/ink/setup-app.js +8 -4
  13. package/src/global/ink/setup-state.js +24 -2
  14. package/src/global/orchestrator.js +46 -42
  15. package/src/global/paths.js +13 -0
  16. package/src/global/profile.js +17 -2
  17. package/src/global/runtime/execution-adapters/claude.js +65 -0
  18. package/src/global/runtime/execution-adapters/codex.js +78 -0
  19. package/src/global/runtime/execution-adapters/create-execution-adapter.js +92 -0
  20. package/src/global/runtime/execution-adapters/cursor.js +104 -0
  21. package/src/global/runtime/execution-adapters/index.js +36 -0
  22. package/src/global/runtime/execution-adapters/opencode.js +38 -0
  23. package/src/global/runtime/run-cancel-signal.js +29 -0
  24. package/src/global/runtime/run-cli.js +221 -0
  25. package/src/global/runtime/run-events.js +144 -0
  26. package/src/global/runtime/run-handoff.js +71 -0
  27. package/src/global/runtime/run-liveness.js +28 -0
  28. package/src/global/runtime/run-manager.js +271 -0
  29. package/src/global/runtime/run-profile.js +93 -0
  30. package/src/global/runtime/run-redact.js +66 -0
  31. package/src/global/runtime/run-starting.js +13 -0
  32. package/src/global/runtime/run-store.js +159 -0
  33. package/src/global/runtime/run-supervisor-lock.js +37 -0
  34. package/src/global/runtime/run-supervisor-worker.js +12 -0
  35. package/src/global/runtime/run-supervisor.js +289 -0
  36. package/src/global/runtime/run-types.js +117 -0
  37. package/src/global/setup.js +13 -5
@@ -1,5 +1,6 @@
1
1
  import { stdin as input, stdout as output } from "node:process";
2
2
  import { canUseSetupInk } from "./terminal.js";
3
+ import { isActiveRunState, formatTaskLabel } from "../runtime/run-types.js";
3
4
 
4
5
  export function canUseOrchestratorShell({
5
6
  interactive = Boolean(input.isTTY && output.isTTY),
@@ -12,24 +13,137 @@ export function canUseOrchestratorShell({
12
13
 
13
14
  export const ORCHESTRATOR_VIEWS = {
14
15
  HOME: "home",
16
+ ACTIVE_RUNS: "active-runs",
17
+ RECENT_RUNS: "recent-runs",
18
+ RUN_DETAIL: "run-detail",
19
+ PROVIDERS: "providers",
20
+ LAUNCH: "launch",
15
21
  DIAGNOSTICS: "diagnostics",
16
- AGENTS: "agents",
17
- PROFILE: "profile",
18
- PLAN: "plan",
19
- CONFIRM: "confirm",
20
- HELP: "help",
21
- INTELLIGENCE: "intelligence"
22
+ HELP: "help"
22
23
  };
23
24
 
24
25
  export const ORCHESTRATOR_MENU = [
25
- { id: "status", label: "Diagnostics", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS },
26
- { id: "agents", label: "Agents", view: ORCHESTRATOR_VIEWS.AGENTS },
27
- { id: "intelligence", label: "Intelligence", view: ORCHESTRATOR_VIEWS.INTELLIGENCE },
28
- { id: "profile", label: "Profile", view: ORCHESTRATOR_VIEWS.PROFILE },
29
- { id: "plan-setup", label: "Plan setup", view: ORCHESTRATOR_VIEWS.PLAN, action: "setup" },
26
+ { id: "active", label: "Active runs", view: ORCHESTRATOR_VIEWS.ACTIVE_RUNS },
27
+ { id: "recent", label: "Recent runs", view: ORCHESTRATOR_VIEWS.RECENT_RUNS },
28
+ { id: "providers", label: "Providers", view: ORCHESTRATOR_VIEWS.PROVIDERS },
29
+ { id: "launch", label: "Launch run", view: ORCHESTRATOR_VIEWS.LAUNCH, action: "launch" },
30
+ { id: "diagnostics", label: "Diagnostics", view: ORCHESTRATOR_VIEWS.DIAGNOSTICS },
30
31
  { id: "help", label: "Help", view: ORCHESTRATOR_VIEWS.HELP }
31
32
  ];
32
33
 
34
+ export const LAUNCH_WIZARD_STEPS = {
35
+ AGENT: "agent",
36
+ TASK: "task",
37
+ MODEL: "model",
38
+ PERMISSIONS: "permissions",
39
+ CONFIRM: "confirm"
40
+ };
41
+
42
+ export const LAUNCH_PERMISSION_OPTIONS = [
43
+ { id: "default", label: "Default (agent prompts)", permissions: [] },
44
+ { id: "force", label: "Force / auto-approve", permissions: ["force"] },
45
+ { id: "yolo", label: "YOLO / skip permissions", permissions: ["yolo"] }
46
+ ];
47
+
48
+ export function resolveLaunchableAgents(providers = []) {
49
+ return providers
50
+ .filter((provider) => provider.launchable)
51
+ .map((provider) => provider.id);
52
+ }
53
+
54
+ export function createLaunchDraft() {
55
+ return {
56
+ agentId: null,
57
+ task: "",
58
+ model: "",
59
+ permissionIndex: 0
60
+ };
61
+ }
62
+
63
+ export function resolveLaunchPermissions(draft) {
64
+ return LAUNCH_PERMISSION_OPTIONS[draft.permissionIndex]?.permissions ?? [];
65
+ }
66
+
67
+ export function advanceLaunchWizardStep(currentStep) {
68
+ switch (currentStep) {
69
+ case LAUNCH_WIZARD_STEPS.AGENT:
70
+ return LAUNCH_WIZARD_STEPS.TASK;
71
+ case LAUNCH_WIZARD_STEPS.TASK:
72
+ return LAUNCH_WIZARD_STEPS.MODEL;
73
+ case LAUNCH_WIZARD_STEPS.MODEL:
74
+ return LAUNCH_WIZARD_STEPS.PERMISSIONS;
75
+ case LAUNCH_WIZARD_STEPS.PERMISSIONS:
76
+ return LAUNCH_WIZARD_STEPS.CONFIRM;
77
+ default:
78
+ return LAUNCH_WIZARD_STEPS.CONFIRM;
79
+ }
80
+ }
81
+
82
+ export function retreatLaunchWizardStep(currentStep) {
83
+ switch (currentStep) {
84
+ case LAUNCH_WIZARD_STEPS.CONFIRM:
85
+ return LAUNCH_WIZARD_STEPS.PERMISSIONS;
86
+ case LAUNCH_WIZARD_STEPS.PERMISSIONS:
87
+ return LAUNCH_WIZARD_STEPS.MODEL;
88
+ case LAUNCH_WIZARD_STEPS.MODEL:
89
+ return LAUNCH_WIZARD_STEPS.TASK;
90
+ case LAUNCH_WIZARD_STEPS.TASK:
91
+ return LAUNCH_WIZARD_STEPS.AGENT;
92
+ default:
93
+ return LAUNCH_WIZARD_STEPS.AGENT;
94
+ }
95
+ }
96
+
97
+ export function formatLaunchWizardLines({
98
+ step,
99
+ draft,
100
+ launchableAgents,
101
+ agentIndex,
102
+ permissionIndex
103
+ }) {
104
+ const lines = [`Step: ${step}`];
105
+
106
+ if (step === LAUNCH_WIZARD_STEPS.AGENT) {
107
+ lines.push("Select agent:");
108
+ for (const [index, agentId] of launchableAgents.entries()) {
109
+ const marker = index === agentIndex ? "›" : " ";
110
+ lines.push(`${marker} ${agentId}`);
111
+ }
112
+ return lines;
113
+ }
114
+
115
+ if (step === LAUNCH_WIZARD_STEPS.TASK) {
116
+ lines.push(`Agent: ${draft.agentId ?? "—"}`);
117
+ lines.push(`Task: ${draft.task || "(type your task)"}`);
118
+ lines.push("Enter to continue · Backspace to edit");
119
+ return lines;
120
+ }
121
+
122
+ if (step === LAUNCH_WIZARD_STEPS.MODEL) {
123
+ lines.push(`Agent: ${draft.agentId ?? "—"}`);
124
+ lines.push(`Task length: ${draft.task.length} chars`);
125
+ lines.push(`Model: ${draft.model || "(default — press Enter)"}`);
126
+ lines.push("Type model alias or Enter for default");
127
+ return lines;
128
+ }
129
+
130
+ if (step === LAUNCH_WIZARD_STEPS.PERMISSIONS) {
131
+ lines.push("Permissions:");
132
+ for (const [index, option] of LAUNCH_PERMISSION_OPTIONS.entries()) {
133
+ const marker = index === permissionIndex ? "›" : " ";
134
+ lines.push(`${marker} ${option.label}`);
135
+ }
136
+ return lines;
137
+ }
138
+
139
+ lines.push(`Agent: ${draft.agentId ?? "—"}`);
140
+ lines.push(`Task length: ${draft.task.length} chars (content not stored)`);
141
+ lines.push(`Model: ${draft.model || "default"}`);
142
+ lines.push(`Permissions: ${LAUNCH_PERMISSION_OPTIONS[permissionIndex]?.label ?? "default"}`);
143
+ lines.push("Enter to launch · Esc to go back");
144
+ return lines;
145
+ }
146
+
33
147
  export function resolveMenuItem(menuIndex) {
34
148
  return ORCHESTRATOR_MENU[menuIndex] ?? null;
35
149
  }
@@ -43,6 +157,29 @@ export function shiftMenuIndex(currentIndex, direction, menuLength = ORCHESTRATO
43
157
  return Math.min(menuLength - 1, Math.max(0, currentIndex + delta));
44
158
  }
45
159
 
160
+ export function formatRunLines(runs, { emptyMessage = "No runs." } = {}) {
161
+ if (!runs || runs.length === 0) return [emptyMessage];
162
+
163
+ return runs.map((run) => {
164
+ const state = run.state.padEnd(12);
165
+ const agent = run.agentId.padEnd(10);
166
+ return `${run.runId} ${state} ${agent} ${formatTaskLabel(run)}`;
167
+ });
168
+ }
169
+
170
+ export function formatProviderLines(providers) {
171
+ return providers.map((provider) => {
172
+ const status = provider.launchable
173
+ ? "launchable"
174
+ : provider.compatible
175
+ ? "auditable"
176
+ : provider.available
177
+ ? "limited"
178
+ : "missing";
179
+ return `${provider.label.padEnd(14)} ${status.padEnd(12)} ${provider.reason ?? ""}`.trimEnd();
180
+ });
181
+ }
182
+
46
183
  export function formatDiagnosticsLines(diagnostics) {
47
184
  const summary = diagnostics?.diagnostics;
48
185
  const lines = [
@@ -53,9 +190,6 @@ export function formatDiagnosticsLines(diagnostics) {
53
190
  `Unknown: ${summary?.unknown ?? 0}`,
54
191
  `Errors: ${summary?.errors ?? 0}`,
55
192
  "",
56
- "Intelligence availability",
57
- ...formatIntelligenceLines(diagnostics),
58
- "",
59
193
  "Agent capabilities",
60
194
  ...formatAgentStatusLines(diagnostics?.capabilities ?? [])
61
195
  ];
@@ -79,71 +213,68 @@ export function formatAgentStatusLines(capabilities) {
79
213
  });
80
214
  }
81
215
 
82
- export function formatProfileLines(profileJson) {
216
+ export function formatRunDetailLines(run, events = []) {
217
+ if (!run) return ["Run not found."];
218
+
83
219
  const lines = [
84
- `Coordinator: ${profileJson.coordinator ?? "none"}`,
85
- `Default agents: ${formatAgentsLabel(profileJson.defaultAgents)}`,
86
- `Apply mode: ${profileJson.applyMode}`,
87
- `Preferred backend: ${profileJson.preferredBackend ?? "auto"}`,
88
- `Preferred model: ${profileJson.preferredModel ?? "auto"}`,
89
- `Cloud consent preference: ${profileJson.cloudConsent ? "recorded (session --cloud-consent still required)" : "no"}`,
90
- `Token budget: ${profileJson.tokenBudget ?? "none"}`
220
+ `Run: ${run.runId}`,
221
+ `Agent: ${run.agentId} (${run.provider})`,
222
+ `State: ${run.state}`,
223
+ `Model: ${run.model ?? "default"}`,
224
+ `Cwd: ${run.cwd}`,
225
+ `Started: ${run.startedAt}`,
226
+ `Updated: ${run.updatedAt}`
91
227
  ];
92
228
 
93
- if (profileJson.sources.global) {
94
- lines.push(`Global: ${profileJson.sources.global}`);
95
- }
229
+ if (run.completedAt) lines.push(`Completed: ${run.completedAt}`);
230
+ if (run.tokenUsage) lines.push(`Tokens: ${JSON.stringify(run.tokenUsage)}`);
231
+ if (run.error) lines.push(`Error: ${run.error}`);
232
+ if (run.tools?.length) lines.push(`Tools: ${run.tools.join(", ")}`);
96
233
 
97
- if (profileJson.sources.project) {
98
- lines.push(`Project: ${profileJson.sources.project}`);
234
+ if (events.length > 0) {
235
+ lines.push("", "Recent events");
236
+ for (const event of events.slice(-8)) {
237
+ if (event.parseError) continue;
238
+ lines.push(` ${event.type} ${summarizeEvent(event)}`);
239
+ }
99
240
  }
100
241
 
101
- lines.push(`Precedence: ${profileJson.sources.precedence}`);
102
242
  return lines;
103
243
  }
104
244
 
105
- export function formatIntelligenceLines(diagnostics) {
106
- const intelligence = diagnostics?.intelligence;
107
- if (!intelligence) {
108
- return ["Intelligence layer unavailable."];
109
- }
110
-
111
- const lines = [
112
- `Local available: ${intelligence.summary.localAvailable ? "yes" : "no"}`,
113
- `Cloud authenticated: ${intelligence.summary.cloudAuthenticated ? "yes" : "no"}`,
114
- `Routing: ${intelligence.routingPreview?.reason ?? "n/a"}`,
115
- `Can invoke: ${intelligence.routingPreview?.canInvoke ? "yes" : "no"}`,
116
- ""
245
+ export function formatDashboardSnapshot(dashboard) {
246
+ const active = dashboard?.activeRuns?.length ?? 0;
247
+ const recent = dashboard?.recentRuns?.length ?? 0;
248
+ const auditable = (dashboard?.providers ?? []).filter((entry) => entry.compatible).length;
249
+ return [
250
+ `Active runs: ${active}`,
251
+ `Recent runs: ${recent}`,
252
+ `Auditable providers: ${auditable}/${dashboard?.providers?.length ?? 0}`
117
253
  ];
118
-
119
- for (const backend of intelligence.backends ?? []) {
120
- lines.push(
121
- `${backend.label.padEnd(14)} ${backend.state.padEnd(14)} models=${backend.models?.length ?? 0}`
122
- );
123
- }
124
-
125
- return lines;
126
254
  }
127
255
 
128
- export function formatPlanLines(plan) {
129
- const lines = [`Action: ${plan.action}`, ""];
130
- for (const step of plan.steps) {
131
- lines.push(` • ${step}`);
132
- }
256
+ export function selectRunFromList(runs, index) {
257
+ return runs[index] ?? null;
258
+ }
133
259
 
134
- if (plan.warnings.length > 0) {
135
- lines.push("", "Warnings:");
136
- for (const warning of plan.warnings) {
137
- lines.push(` ! ${warning}`);
138
- }
139
- }
260
+ export function filterInspectableRuns(runs) {
261
+ return runs ?? [];
262
+ }
140
263
 
141
- return lines;
264
+ export function isRunCancellable(run) {
265
+ return run && isActiveRunState(run.state);
142
266
  }
143
267
 
144
- function formatAgentsLabel(agents) {
145
- if (agents === "detected") return "detected";
146
- if (agents === "all") return "all";
147
- if (Array.isArray(agents)) return agents.join(", ");
148
- return String(agents);
268
+ function summarizeEvent(event) {
269
+ if (event.type === "agent.tool_call") {
270
+ return event.data?.tool_name ?? event.data?.name ?? "tool";
271
+ }
272
+ if (event.type === "process.stdout" || event.type === "process.stderr") {
273
+ const line = event.data?.line ?? "";
274
+ return line.length > 60 ? `${line.slice(0, 59)}…` : line;
275
+ }
276
+ if (event.type === "run.completed" || event.type === "run.failed") {
277
+ return `exit=${event.data?.exitCode ?? "n/a"}`;
278
+ }
279
+ return "";
149
280
  }
@@ -8,6 +8,7 @@ export async function runOrchestratorInk({
8
8
  packageRoot,
9
9
  packageName,
10
10
  cliVersion,
11
+ hasGlobalState = false,
11
12
  renderImpl = render
12
13
  }) {
13
14
  return new Promise((resolve) => {
@@ -18,6 +19,7 @@ export async function runOrchestratorInk({
18
19
  packageRoot,
19
20
  packageName,
20
21
  cliVersion,
22
+ hasGlobalState,
21
23
  onComplete: resolve
22
24
  })
23
25
  );
@@ -15,6 +15,7 @@ export async function runSetupInk({
15
15
  packageName,
16
16
  cliVersion,
17
17
  dryRun = false,
18
+ onboarding = false,
18
19
  preflight = true,
19
20
  yes = false,
20
21
  confirm = false,
@@ -33,6 +34,7 @@ export async function runSetupInk({
33
34
  packageName,
34
35
  cliVersion,
35
36
  dryRun,
37
+ onboarding,
36
38
  onComplete: resolve
37
39
  })
38
40
  );
@@ -56,8 +56,8 @@ function Footer({ children }) {
56
56
  return React.createElement(Text, { dimColor: true }, children);
57
57
  }
58
58
 
59
- function Splash({ compact }) {
60
- const lines = formatInkSplashLines({ compact });
59
+ function Splash({ compact, onboarding = false }) {
60
+ const lines = formatInkSplashLines({ compact, onboarding });
61
61
  const logoLineCount = compact ? BRAND.compactLogo.length : BRAND.asciiLogo.length;
62
62
 
63
63
  return React.createElement(Box, { flexDirection: "column", marginBottom: 1 },
@@ -71,7 +71,7 @@ function Splash({ compact }) {
71
71
  if (line === BRAND.tagline) {
72
72
  return React.createElement(Text, { key: `line-${index}`, color: INK_COLORS.muted }, line);
73
73
  }
74
- if (line === BRAND.splashHint) {
74
+ if (line === BRAND.splashHint || line.includes("Esc to exit") || line.includes("Press Enter")) {
75
75
  return React.createElement(Text, { key: `line-${index}`, dimColor: true }, line);
76
76
  }
77
77
  if (line === "") {
@@ -89,6 +89,7 @@ export function SetupApp({
89
89
  packageName,
90
90
  cliVersion,
91
91
  dryRun = false,
92
+ onboarding = false,
92
93
  onComplete
93
94
  }) {
94
95
  const { exit } = useApp();
@@ -243,7 +244,10 @@ export function SetupApp({
243
244
  const detectPanel = formatInkDetectPanel({ adapters, detected });
244
245
 
245
246
  return React.createElement(Box, { flexDirection: "column" },
246
- step === SETUP_STEPS.SPLASH && React.createElement(Splash, { compact: useCompactSplash }),
247
+ step === SETUP_STEPS.SPLASH && React.createElement(Splash, {
248
+ compact: useCompactSplash,
249
+ onboarding
250
+ }),
247
251
  step !== SETUP_STEPS.SPLASH && React.createElement(Header),
248
252
  step === SETUP_STEPS.DETECT && React.createElement(Panel, { title: WIZARD_COPY.detectTitle },
249
253
  detectPanel.split("\n")
@@ -1,4 +1,12 @@
1
- import { AGENT_HINTS, BRAND, formatCliCommand, getAgentLabel, PREFERRED_CLI, WIZARD_COPY } from "../brand/index.js";
1
+ import {
2
+ AGENT_HINTS,
3
+ BRAND,
4
+ ONBOARDING_COPY,
5
+ formatCliCommand,
6
+ getAgentLabel,
7
+ PREFERRED_CLI,
8
+ WIZARD_COPY
9
+ } from "../brand/index.js";
2
10
  import { formatAgentMultiselectHint } from "../clack/theme.js";
3
11
 
4
12
  export const SETUP_STEPS = {
@@ -16,8 +24,22 @@ export function shouldUseCompactSplashLogo(columns) {
16
24
  return columns < fullWidth + 4;
17
25
  }
18
26
 
19
- export function formatInkSplashLines({ compact = false } = {}) {
27
+ export function formatInkSplashLines({ compact = false, onboarding = false } = {}) {
20
28
  const logo = compact ? BRAND.compactLogo : BRAND.asciiLogo;
29
+ if (onboarding) {
30
+ return [
31
+ ...logo,
32
+ "",
33
+ BRAND.name,
34
+ BRAND.tagline,
35
+ "",
36
+ ONBOARDING_COPY.purpose,
37
+ ONBOARDING_COPY.safety,
38
+ "",
39
+ ONBOARDING_COPY.continueHint
40
+ ];
41
+ }
42
+
21
43
  return [
22
44
  ...logo,
23
45
  "",
@@ -1,11 +1,15 @@
1
1
  import { stdin as input, stdout as output } from "node:process";
2
2
  import { resolveHomeDir } from "./paths.js";
3
- import { runGlobalSetup } from "./global-cli.js";
4
- import { PLAN_ACTIONS, buildReadOnlyDiagnostics, shouldExecutePlan } from "./action-planner.js";
5
3
  import { canUseOrchestratorShell } from "./ink/orchestrator-state.js";
6
4
  import { runOrchestratorInk as defaultRunOrchestratorInk } from "./ink/run-orchestrator-ink.js";
7
5
  import { formatCliCommand } from "./brand/cli.js";
8
6
  import { BRAND } from "./brand/index.js";
7
+ import { buildReadOnlyDiagnostics, shouldExecutePlan } from "./action-planner.js";
8
+ import { runHarnessSetup as defaultRunHarnessSetup } from "./setup.js";
9
+ import {
10
+ INITIAL_EXPERIENCE,
11
+ hasConfiguredGlobalState
12
+ } from "./initial-experience.js";
9
13
 
10
14
  export { canUseOrchestratorShell };
11
15
 
@@ -24,68 +28,68 @@ export async function runOrchestratorShell({
24
28
  packageManifest,
25
29
  workspaceRoot,
26
30
  interactive = Boolean(input.isTTY && output.isTTY),
27
- runOrchestratorInkImpl = defaultRunOrchestratorInk
31
+ initialMode = INITIAL_EXPERIENCE.DASHBOARD,
32
+ shellCapable = canUseOrchestratorShell({ interactive }),
33
+ runOrchestratorInkImpl = defaultRunOrchestratorInk,
34
+ runHarnessSetupImpl = defaultRunHarnessSetup
28
35
  }) {
29
36
  if (!interactive) {
30
37
  throw new Error(
31
- `Non-interactive shell requires an explicit command. Try ${formatCliCommand("help")} or ${formatCliCommand("setup --yes")}.`
38
+ `Non-interactive shell requires an explicit command. Try ${formatCliCommand("help")} or ${formatCliCommand("runs list")}.`
32
39
  );
33
40
  }
34
41
 
35
- if (!canUseOrchestratorShell({ interactive })) {
42
+ if (!shellCapable) {
36
43
  throw new Error(
37
- `Interactive shell requires a capable TTY. Use ${formatCliCommand("setup --simple")} or explicit commands.`
44
+ `Interactive shell requires a capable TTY. Use ${formatCliCommand("runs list")} or explicit commands.`
38
45
  );
39
46
  }
40
47
 
41
48
  const homeDir = resolveHomeDir();
49
+ let setupOutcome = null;
50
+
51
+ if (initialMode === INITIAL_EXPERIENCE.ONBOARDING) {
52
+ setupOutcome = await runHarnessSetupImpl({
53
+ packageRoot,
54
+ packageName: packageManifest.name,
55
+ cliVersion: packageManifest.version,
56
+ homeDir,
57
+ workspaceRoot,
58
+ onboarding: true,
59
+ interactive: true
60
+ });
61
+
62
+ if (setupOutcome?.cancelled) {
63
+ return {
64
+ cancelled: true,
65
+ wrote: false,
66
+ action: null,
67
+ initialMode,
68
+ setup: setupOutcome
69
+ };
70
+ }
71
+ }
72
+
42
73
  const outcome = await runOrchestratorInkImpl({
43
74
  homeDir,
44
75
  workspaceRoot,
45
76
  packageRoot,
46
77
  packageName: packageManifest.name,
47
- cliVersion: packageManifest.version
78
+ cliVersion: packageManifest.version,
79
+ hasGlobalState: hasConfiguredGlobalState(homeDir)
48
80
  });
49
81
 
50
82
  if (outcome.error) {
51
83
  throw outcome.error;
52
84
  }
53
85
 
54
- if (outcome.cancelled) {
55
- return { cancelled: true, wrote: false };
56
- }
57
-
58
- if (outcome.confirmed && outcome.action === PLAN_ACTIONS.SETUP) {
59
- const setupOutcome = await runGlobalSetup(
60
- {
61
- cwd: workspaceRoot,
62
- adapters: null,
63
- components: null,
64
- noDefaultComponents: false,
65
- dryRun: false,
66
- yes: false,
67
- confirm: false,
68
- preflight: true,
69
- preflightExplicit: false,
70
- yesExplicit: false,
71
- confirmExplicit: false,
72
- json: false,
73
- interactive: true,
74
- simple: false
75
- },
76
- packageManifest,
77
- packageRoot
78
- );
79
-
80
- return {
81
- cancelled: Boolean(setupOutcome.cancelled),
82
- wrote: !setupOutcome.cancelled && !setupOutcome.result?.dryRun,
83
- action: PLAN_ACTIONS.SETUP,
84
- setupOutcome
85
- };
86
- }
87
-
88
- return { cancelled: false, wrote: false, action: outcome.action ?? null };
86
+ return {
87
+ cancelled: Boolean(outcome.cancelled),
88
+ wrote: Boolean(setupOutcome && !setupOutcome.cancelled),
89
+ action: outcome.action ?? null,
90
+ initialMode,
91
+ setup: setupOutcome
92
+ };
89
93
  }
90
94
 
91
95
  export async function runOrchestratorDiagnostics({
@@ -17,7 +17,20 @@ export function harnessHomePaths(homeDir) {
17
17
  policyPath: join(root, "policy.json"),
18
18
  profilePath: join(root, "profile.json"),
19
19
  historyPath: join(root, "history.jsonl"),
20
+ runsDir: join(root, "runs"),
20
21
  coreDir: join(root, "core"),
21
22
  backupsDir: join(root, "backups")
22
23
  };
23
24
  }
25
+
26
+ export function runPaths(homeDir, runId) {
27
+ const { runsDir } = harnessHomePaths(homeDir);
28
+ const runDir = join(runsDir, runId);
29
+
30
+ return {
31
+ runDir,
32
+ statePath: join(runDir, "state.json"),
33
+ eventsPath: join(runDir, "events.jsonl"),
34
+ transcriptPath: join(runDir, "transcript.jsonl")
35
+ };
36
+ }
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { harnessHomePaths } from "./paths.js";
5
5
  import { AGENT_CAPABILITY_IDS } from "./agent-capabilities/index.js";
6
6
  import { classifyCustomBaseUrl, isValidEnvironmentName } from "./intelligence/custom-url.js";
7
+ import { validateRuntimeProfile, RUNTIME_PROFILE_KEYS } from "./runtime/run-profile.js";
7
8
 
8
9
  export const PROFILE_KEYS = new Set([
9
10
  "coordinator",
@@ -16,7 +17,8 @@ export const PROFILE_KEYS = new Set([
16
17
  "tokenBudget",
17
18
  "stableContextBudget",
18
19
  "requestContextBudget",
19
- "customProviders"
20
+ "customProviders",
21
+ ...RUNTIME_PROFILE_KEYS
20
22
  ]);
21
23
 
22
24
  export const DEFAULT_PROFILE = {
@@ -30,7 +32,13 @@ export const DEFAULT_PROFILE = {
30
32
  tokenBudget: null,
31
33
  stableContextBudget: null,
32
34
  requestContextBudget: null,
33
- customProviders: []
35
+ customProviders: [],
36
+ agentAliases: {},
37
+ modelAliases: {},
38
+ defaultPermissions: [],
39
+ defaultRuntimeAgent: null,
40
+ defaultRuntimeModel: null,
41
+ captureTranscript: false
34
42
  };
35
43
 
36
44
  const APPLY_MODES = new Set(["prompt", "confirm"]);
@@ -105,6 +113,12 @@ export function buildProfileJson(resolved) {
105
113
  stableContextBudget: profile.stableContextBudget,
106
114
  requestContextBudget: profile.requestContextBudget,
107
115
  customProviders: sanitizeCustomProviders(profile.customProviders),
116
+ agentAliases: profile.agentAliases ?? {},
117
+ modelAliases: profile.modelAliases ?? {},
118
+ defaultPermissions: profile.defaultPermissions ?? [],
119
+ defaultRuntimeAgent: profile.defaultRuntimeAgent ?? null,
120
+ defaultRuntimeModel: profile.defaultRuntimeModel ?? null,
121
+ captureTranscript: profile.captureTranscript ?? false,
108
122
  sources: {
109
123
  global: sources.global,
110
124
  project: sources.project,
@@ -187,6 +201,7 @@ function validateProfile(profile) {
187
201
 
188
202
  validateNoSecrets(profile);
189
203
  validateCustomProviders(profile.customProviders);
204
+ validateRuntimeProfile(profile);
190
205
  }
191
206
 
192
207
  /**