@kruntime/komputer 0.1.0 → 0.1.2

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.
@@ -96,7 +96,12 @@ export function resultSummary(value) {
96
96
  return { type: typeof value };
97
97
  }
98
98
  function approvalActionKey(hook, current) {
99
- return sha256(stableJson({ hook, current }));
99
+ return sha256(stableJson({ hook, current: approvalActionCurrent(current) }));
100
+ }
101
+ function approvalActionCurrent(current) {
102
+ const copy = { ...current };
103
+ delete copy.execToolUseId;
104
+ return copy;
100
105
  }
101
106
  function stableJson(value) {
102
107
  if (value === null || typeof value !== 'object')
@@ -1 +1 @@
1
- {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../../../../../packages/komputer/src/agent/approvals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"approvals.js","sourceRoot":"","sources":["../../../../../packages/komputer/src/agent/approvals.ts"],"names":[],"mappings":"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"}
@@ -37,6 +37,7 @@ export interface ExecOptions {
37
37
  }
38
38
  type ExecShellOptions = ExecOptions & {
39
39
  forceRemote?: boolean;
40
+ execToolUseId?: string;
40
41
  };
41
42
  interface TurnEndPayload {
42
43
  turnId: string;
@@ -594,10 +594,24 @@ export class AgentSession {
594
594
  return this.send(current.content, recordField(current, 'options') || {});
595
595
  }
596
596
  if (hook === 'shell.before' || hook === 'cmd.before') {
597
- return this.execInternal(stringField(current, 'shellScript') || stringField(current, 'script'), {
597
+ const result = await this.execInternal(stringField(current, 'shellScript') || stringField(current, 'script'), {
598
598
  by: stringField(current, 'by') || `approval:${approval.id}`,
599
599
  foreground: typeof current.foreground === 'boolean' ? current.foreground : undefined,
600
600
  });
601
+ const execToolUseId = stringField(current, 'execToolUseId');
602
+ if (execToolUseId) {
603
+ const bounded = this.boundShellResult(result);
604
+ await this.appendExecToolExchange(execToolUseMessageFromApproval(current, execToolUseId), execToolUseId, bounded, stringField(current, 'by') || `approval:${approval.id}`);
605
+ await this.emit({
606
+ type: 'tool',
607
+ status: shellToolEventStatus(bounded),
608
+ tool: 'shell',
609
+ result: bounded,
610
+ opId: execToolUseId,
611
+ approvalId: approval.id,
612
+ });
613
+ }
614
+ return result;
601
615
  }
602
616
  if (hook === 'tool.before') {
603
617
  return this.command(stringField(current, 'command'), recordField(current, 'input') || {}, {
@@ -1060,10 +1074,20 @@ export class AgentSession {
1060
1074
  };
1061
1075
  try {
1062
1076
  await this.emit({ type: 'tool', status: 'requested', tool: 'shell', input: toolUseMessage.content[0]?.input, opId: toolUseId });
1063
- const result = await this.execShell(command, options);
1077
+ const result = await this.execShell(command, { ...options, execToolUseId: toolUseId });
1064
1078
  const bounded = this.boundShellResult(result);
1065
- await this.appendExecToolExchange(toolUseMessage, toolUseId, bounded, by);
1066
- await this.emit({ type: 'tool', status: bounded.code === 0 ? 'completed' : 'failed', tool: 'shell', result: bounded, opId: toolUseId });
1079
+ const toolStatus = shellToolEventStatus(bounded);
1080
+ if (toolStatus !== 'waiting_approval') {
1081
+ await this.appendExecToolExchange(toolUseMessage, toolUseId, bounded, by);
1082
+ }
1083
+ await this.emit({
1084
+ type: 'tool',
1085
+ status: toolStatus,
1086
+ tool: 'shell',
1087
+ result: bounded,
1088
+ opId: toolUseId,
1089
+ approvalId: stringField(recordField(bounded, 'approval') || {}, 'id'),
1090
+ });
1067
1091
  return bounded;
1068
1092
  }
1069
1093
  catch (error) {
@@ -1134,7 +1158,7 @@ export class AgentSession {
1134
1158
  const shell = String(command);
1135
1159
  const foreground = options.foreground ?? !shell.trimEnd().endsWith('&');
1136
1160
  let script = foreground ? shell : shell.trimEnd().replace(/&\s*$/, '');
1137
- const beforeShell = await this.runHook('shell.before', { script, cwd: this.hookCwd(), by: normalizeBy(options.by) });
1161
+ const beforeShell = await this.runHook('shell.before', { script, cwd: this.hookCwd(), by: normalizeBy(options.by), foreground, execToolUseId: options.execToolUseId });
1138
1162
  if (beforeShell.rejected) {
1139
1163
  await this.trace('process.rejected', { script, reason: beforeShell.reason });
1140
1164
  if (beforeShell.waitingApproval) {
@@ -1184,6 +1208,7 @@ export class AgentSession {
1184
1208
  by: normalizeBy(options.by),
1185
1209
  foreground: foreground && !concrete.background,
1186
1210
  background: !foreground || concrete.background,
1211
+ execToolUseId: options.execToolUseId,
1187
1212
  });
1188
1213
  if (beforeCmd.rejected) {
1189
1214
  await this.trace('process.rejected', { cmd: concrete.cmd, args: concrete.args, reason: beforeCmd.reason });
@@ -2621,6 +2646,9 @@ export class AgentSession {
2621
2646
  throw new Error(`command not found: ${name}`);
2622
2647
  if (command.agent && command.agent !== this.agentName)
2623
2648
  throw new Error(`command ${name} belongs to agent ${command.agent}`);
2649
+ if (isCommandHelpInput(input)) {
2650
+ return { type: 'stdout', text: `${commandHelpText(name, command)}\n`, code: 0 };
2651
+ }
2624
2652
  const by = normalizeBy(options.by || (typeof input.by === 'string' ? input.by : undefined));
2625
2653
  const toolName = `command:${name}`;
2626
2654
  const before = await this.runHook('tool.before', { tool: toolName, command: name, input, by });
@@ -2637,7 +2665,16 @@ export class AgentSession {
2637
2665
  throw new Error(authority.reason);
2638
2666
  }
2639
2667
  const toolUseId = id('tool');
2640
- const ctx = this.runtime.runtimeContext(this, { kind: 'command', name, input: currentInput, args: currentInput });
2668
+ const parsed = parseCommandInput(name, command, currentInput);
2669
+ const ctx = this.runtime.runtimeContext(this, {
2670
+ kind: 'command',
2671
+ name,
2672
+ input: currentInput,
2673
+ args: parsed.args,
2674
+ argv: parsed.argv,
2675
+ stdin: parsed.stdin,
2676
+ env: parsed.env,
2677
+ });
2641
2678
  await this.emit({ type: 'tool', status: 'requested', tool: toolName, input: currentInput, opId: toolUseId });
2642
2679
  await this.trace('tool.started', { name, input: currentInput, by, toolUseId });
2643
2680
  let result;
@@ -2652,20 +2689,30 @@ export class AgentSession {
2652
2689
  result = invocationMode ? await this.withTemporaryMode(invocationMode, runShell) : await runShell();
2653
2690
  }
2654
2691
  else if (command.kind === 'markdown') {
2655
- const args = Object.keys(currentInput).length ? `\n\nInput:\n${JSON.stringify(currentInput, null, 2)}` : '';
2692
+ const prompt = String(command.prompt ?? '');
2693
+ const rendered = renderMarkdownCommandPrompt(prompt, parsed.args, parsed.stdin);
2694
+ const args = hasCommandTemplate(prompt) || Object.keys(currentInput).length === 0
2695
+ ? ''
2696
+ : `\n\nInput:\n${JSON.stringify(currentInput, null, 2)}`;
2656
2697
  const invocationMode = command.readonly === true
2657
2698
  ? 'readonly'
2658
2699
  : typeof command.mode === 'string'
2659
2700
  ? command.mode
2660
2701
  : undefined;
2661
- const runMarkdown = () => this.run(`${String(command.prompt ?? '')}${args}`, { by: options.by || `command:${name}`, model: typeof command.model === 'string' ? command.model : undefined });
2702
+ const runMarkdown = () => this.run(`${rendered}${args}`, { by: options.by || `command:${name}`, model: typeof command.model === 'string' ? command.model : undefined });
2662
2703
  result = invocationMode ? await this.withTemporaryMode(invocationMode, runMarkdown) : await runMarkdown();
2663
2704
  }
2664
2705
  else {
2665
- if (!command.handler)
2666
- throw new Error(`command has no handler: ${name}`);
2667
- const handler = command.handler;
2668
- result = await this.withFsActor(options.by ? by : `command:${name}`, () => Promise.resolve(handler(ctx, currentInput)));
2706
+ const main = command.main ?? command.handler;
2707
+ if (!main)
2708
+ throw new Error(`command has no main: ${name}`);
2709
+ const runModule = () => this.withFsActor(options.by ? by : `command:${name}`, () => Promise.resolve(main(ctx, parsed.args)));
2710
+ const invocationMode = command.readonly === true
2711
+ ? 'readonly'
2712
+ : typeof command.mode === 'string'
2713
+ ? command.mode
2714
+ : undefined;
2715
+ result = invocationMode ? await this.withTemporaryMode(invocationMode, runModule) : await runModule();
2669
2716
  }
2670
2717
  const after = await this.runHook('tool.after', { tool: toolName, command: name, input: currentInput, result, by });
2671
2718
  if (after.rejected) {
@@ -2967,6 +3014,169 @@ function commandErrorText(command, error) {
2967
3014
  function isHelpArg(value) {
2968
3015
  return value === '--help' || value === '-h';
2969
3016
  }
3017
+ function isCommandHelpInput(input) {
3018
+ const argv = stringArrayField(input, 'argv') || [];
3019
+ return argv.length === 1 && isHelpArg(argv[0]);
3020
+ }
3021
+ function parseCommandInput(name, command, input) {
3022
+ const argv = stringArrayField(input, 'argv') || [];
3023
+ const stdin = typeof input.stdin === 'string' ? input.stdin : '';
3024
+ const envRecord = recordField(input, 'env');
3025
+ const env = envRecord ? stringRecord(envRecord) : undefined;
3026
+ if (command.args === 'none') {
3027
+ const extra = argv.filter((arg) => arg !== '--');
3028
+ if (extra.length)
3029
+ throw new Error(`${name}: unexpected argument: ${extra[0]}`);
3030
+ return { argv, stdin, env, args: {} };
3031
+ }
3032
+ if (!command.args || command.args === 'argv')
3033
+ return { argv, stdin, env, args: input };
3034
+ return { argv, stdin, env, args: parseStructuredCommandArgs(name, command.args, input, argv) };
3035
+ }
3036
+ function parseStructuredCommandArgs(name, specs, input, argv) {
3037
+ const parsed = {};
3038
+ const options = commandOptionSpecs(specs);
3039
+ for (const [key, spec] of Object.entries(specs)) {
3040
+ if ('default' in spec)
3041
+ parsed[key] = spec.default;
3042
+ else if ('defaultValue' in spec)
3043
+ parsed[key] = spec.defaultValue;
3044
+ }
3045
+ for (const [key, value] of Object.entries(input)) {
3046
+ if (key !== 'argv' && key !== 'stdin' && key !== 'env' && specs[key])
3047
+ parsed[key] = value;
3048
+ }
3049
+ const positional = Object.entries(specs)
3050
+ .filter(([, spec]) => typeof spec.position === 'number')
3051
+ .sort((a, b) => Number(a[1].position) - Number(b[1].position));
3052
+ let positionalIndex = 0;
3053
+ for (let index = 0; index < argv.length; index += 1) {
3054
+ const token = argv[index];
3055
+ if (token === '--') {
3056
+ for (const rest of argv.slice(index + 1))
3057
+ assignPositional(name, parsed, positional, positionalIndex++, rest);
3058
+ break;
3059
+ }
3060
+ if (token.startsWith('--')) {
3061
+ const body = token.slice(2);
3062
+ const eq = body.indexOf('=');
3063
+ const option = eq >= 0 ? body.slice(0, eq) : body;
3064
+ const resolved = options.get(option);
3065
+ if (!resolved)
3066
+ throw new Error(`${name}: unsupported option: --${option}`);
3067
+ const [key, spec] = resolved;
3068
+ if (spec.type === 'boolean' && eq < 0) {
3069
+ parsed[key] = true;
3070
+ continue;
3071
+ }
3072
+ const value = eq >= 0 ? body.slice(eq + 1) : argv[++index];
3073
+ if (value === undefined)
3074
+ throw new Error(`${name}: option requires an argument: --${key}`);
3075
+ assignCommandArg(name, parsed, key, spec, value);
3076
+ continue;
3077
+ }
3078
+ const eq = token.indexOf('=');
3079
+ if (eq > 0 && specs[token.slice(0, eq)]) {
3080
+ const key = token.slice(0, eq);
3081
+ assignCommandArg(name, parsed, key, specs[key], token.slice(eq + 1));
3082
+ continue;
3083
+ }
3084
+ assignPositional(name, parsed, positional, positionalIndex++, token);
3085
+ }
3086
+ for (const [key, spec] of Object.entries(specs)) {
3087
+ if (spec.required === true && (parsed[key] === undefined || parsed[key] === '')) {
3088
+ throw new Error(`${name}: missing required argument: --${key}`);
3089
+ }
3090
+ if (parsed[key] !== undefined)
3091
+ parsed[key] = coerceCommandArg(name, key, spec, parsed[key]);
3092
+ }
3093
+ return parsed;
3094
+ }
3095
+ function commandOptionSpecs(specs) {
3096
+ const options = new Map();
3097
+ for (const [key, spec] of Object.entries(specs)) {
3098
+ options.set(key, [key, spec]);
3099
+ const flag = typeof spec.flag === 'string' ? normalizeCommandFlag(spec.flag) : undefined;
3100
+ if (flag)
3101
+ options.set(flag, [key, spec]);
3102
+ }
3103
+ return options;
3104
+ }
3105
+ function normalizeCommandFlag(flag) {
3106
+ return flag.trim().replace(/^-+/, '');
3107
+ }
3108
+ function assignPositional(name, parsed, positional, index, value) {
3109
+ const entry = positional[index];
3110
+ if (!entry)
3111
+ throw new Error(`${name}: unexpected argument: ${String(value)}`);
3112
+ assignCommandArg(name, parsed, entry[0], entry[1], value);
3113
+ }
3114
+ function assignCommandArg(name, parsed, key, spec, value) {
3115
+ const coerced = coerceCommandArg(name, key, spec, value);
3116
+ if (spec.repeat) {
3117
+ const current = Array.isArray(parsed[key]) ? parsed[key] : parsed[key] === undefined ? [] : [parsed[key]];
3118
+ current.push(coerced);
3119
+ parsed[key] = current;
3120
+ return;
3121
+ }
3122
+ parsed[key] = coerced;
3123
+ }
3124
+ function coerceCommandArg(name, key, spec, value) {
3125
+ if (value === undefined)
3126
+ return value;
3127
+ if (spec.type === 'string' || spec.type === 'path')
3128
+ return String(value);
3129
+ if (spec.type === 'number') {
3130
+ const number = typeof value === 'number' ? value : Number(String(value));
3131
+ if (!Number.isFinite(number))
3132
+ throw new Error(`${name}: invalid value for --${key}: expected number`);
3133
+ return number;
3134
+ }
3135
+ if (spec.type === 'boolean') {
3136
+ if (typeof value === 'boolean')
3137
+ return value;
3138
+ const text = String(value).toLowerCase();
3139
+ if (text === 'true' || text === '1' || text === 'yes')
3140
+ return true;
3141
+ if (text === 'false' || text === '0' || text === 'no')
3142
+ return false;
3143
+ throw new Error(`${name}: invalid value for --${key}: expected boolean`);
3144
+ }
3145
+ if (spec.type === 'json') {
3146
+ if (typeof value !== 'string')
3147
+ return value;
3148
+ try {
3149
+ return JSON.parse(value);
3150
+ }
3151
+ catch {
3152
+ throw new Error(`${name}: invalid value for --${key}: expected json`);
3153
+ }
3154
+ }
3155
+ return value;
3156
+ }
3157
+ function stringRecord(record) {
3158
+ const output = {};
3159
+ for (const [key, value] of Object.entries(record)) {
3160
+ if (typeof value === 'string')
3161
+ output[key] = value;
3162
+ }
3163
+ return output;
3164
+ }
3165
+ function hasCommandTemplate(prompt) {
3166
+ return /\{\{\s*(?:arg|args)\.[A-Za-z0-9_.-]+\s*\}\}|\{\{\s*stdin\s*\}\}/.test(prompt);
3167
+ }
3168
+ function renderMarkdownCommandPrompt(prompt, args, stdin) {
3169
+ return prompt.replace(/\{\{\s*((?:arg|args)\.([A-Za-z0-9_.-]+)|stdin)\s*\}\}/g, (_match, token, argName) => {
3170
+ if (token === 'stdin')
3171
+ return stdin;
3172
+ const value = argName ? args[argName] : undefined;
3173
+ if (value === undefined || value === null)
3174
+ return '';
3175
+ if (typeof value === 'string')
3176
+ return value;
3177
+ return JSON.stringify(value);
3178
+ });
3179
+ }
2970
3180
  function isExpiredWorker(worker) {
2971
3181
  const expiresAt = typeof worker.expiresAt === 'string' ? Date.parse(worker.expiresAt) : Number.NaN;
2972
3182
  return Number.isFinite(expiresAt) && expiresAt <= Date.now();
@@ -3010,6 +3220,32 @@ function turnStatus(status) {
3010
3220
  return 'cancelled';
3011
3221
  return 'completed';
3012
3222
  }
3223
+ function shellToolEventStatus(result) {
3224
+ if (result.status === 'waiting_approval')
3225
+ return 'waiting_approval';
3226
+ if (result.status === 'committed' || result.status === 'completed')
3227
+ return 'completed';
3228
+ if (typeof result.code === 'number')
3229
+ return result.code === 0 ? 'completed' : 'failed';
3230
+ return 'failed';
3231
+ }
3232
+ function execToolUseMessageFromApproval(current, toolUseId) {
3233
+ const script = stringField(current, 'shellScript') || stringField(current, 'script');
3234
+ return {
3235
+ role: 'assistant',
3236
+ by: stringField(current, 'by') || 'approval',
3237
+ content: [{
3238
+ type: 'tool_use',
3239
+ id: toolUseId,
3240
+ name: 'shell',
3241
+ input: {
3242
+ script,
3243
+ cwd: stringField(current, 'cwd'),
3244
+ foreground: typeof current.foreground === 'boolean' ? current.foreground : undefined,
3245
+ },
3246
+ }],
3247
+ };
3248
+ }
3013
3249
  function shouldExpandBraces(token) {
3014
3250
  let hasUnquotedBrace = false;
3015
3251
  for (const part of token.parts) {