@kruntime/komputer 0.1.3 → 0.1.4
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.
- package/dist/agent/recovery.js +1 -1
- package/dist/agent/recovery.js.map +1 -1
- package/dist/agent-session.d.ts +4 -0
- package/dist/agent-session.js +136 -15
- package/dist/agent-session.js.map +1 -1
- package/dist/authority.d.ts +4 -2
- package/dist/authority.js +15 -43
- package/dist/authority.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/komputer.d.ts +0 -3
- package/dist/komputer.js +4 -29
- package/dist/komputer.js.map +1 -1
- package/dist/remote.d.ts +13 -0
- package/dist/remote.js +50 -0
- package/dist/remote.js.map +1 -1
- package/dist/shell/builtins/run.d.ts +8 -0
- package/dist/shell/builtins/run.js +13 -0
- package/dist/shell/builtins/run.js.map +1 -1
- package/dist/types.d.ts +0 -4
- package/package.json +1 -1
package/dist/agent/recovery.js
CHANGED
|
@@ -57,7 +57,7 @@ export async function sealUnfinishedToolEvents(runtime) {
|
|
|
57
57
|
cursor: row.cursor,
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
-
if (event.type === 'tool' && (event.status === 'completed' || event.status === 'failed' || event.status === 'denied' || event.status === 'sealed') && typeof event.opId === 'string') {
|
|
60
|
+
if (event.type === 'tool' && (event.status === 'waiting_approval' || event.status === 'completed' || event.status === 'failed' || event.status === 'denied' || event.status === 'sealed') && typeof event.opId === 'string') {
|
|
61
61
|
results.add(event.opId);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../../../packages/komputer/src/agent/recovery.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../../../../../packages/komputer/src/agent/recovery.ts"],"names":[],"mappings":"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"}
|
package/dist/agent-session.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface ExecOptions {
|
|
|
38
38
|
type ExecShellOptions = ExecOptions & {
|
|
39
39
|
forceRemote?: boolean;
|
|
40
40
|
execToolUseId?: string;
|
|
41
|
+
skipCommandAuthority?: boolean;
|
|
41
42
|
};
|
|
42
43
|
interface TurnEndPayload {
|
|
43
44
|
turnId: string;
|
|
@@ -152,6 +153,7 @@ export declare class AgentSession {
|
|
|
152
153
|
hook(type: string, handler: HookHandler): () => void;
|
|
153
154
|
hasHooks(type: string): boolean;
|
|
154
155
|
runHook(type: string, current: Record<string, unknown>): Promise<HookRunResult>;
|
|
156
|
+
private requestAuthorityApproval;
|
|
155
157
|
hookContext(type: string, current: Record<string, unknown>): HookContext<Record<string, unknown>>;
|
|
156
158
|
signal(name: string, data?: unknown, options?: {
|
|
157
159
|
level?: string;
|
|
@@ -162,6 +164,7 @@ export declare class AgentSession {
|
|
|
162
164
|
}): Promise<Record<string, unknown>>;
|
|
163
165
|
consumeApproval(approval: ApprovalRecord): Promise<void>;
|
|
164
166
|
resumeApproval(approval: ApprovalRecord): Promise<unknown>;
|
|
167
|
+
private rejectPausedApprovalTool;
|
|
165
168
|
resumeFsApproval(current: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
166
169
|
close(): Promise<void>;
|
|
167
170
|
detach(): Promise<void>;
|
|
@@ -202,6 +205,7 @@ export declare class AgentSession {
|
|
|
202
205
|
recordInput(record: Record<string, unknown>): Promise<void>;
|
|
203
206
|
private nextQueuedInputId;
|
|
204
207
|
stats(): AgentSessionStats;
|
|
208
|
+
private isReadonlyCommandName;
|
|
205
209
|
exec(command: string, options?: ExecOptions): Promise<Record<string, unknown>>;
|
|
206
210
|
execInternal(command: string, options?: ExecShellOptions): Promise<Record<string, unknown>>;
|
|
207
211
|
execCommand(command: string | {
|
package/dist/agent-session.js
CHANGED
|
@@ -11,13 +11,14 @@ import { id, normalizeBy, now, sleep } from "./utils.js";
|
|
|
11
11
|
import { isGovernableShellScript, isSimpleShellCommand, parseShellCommandSegments, rewriteKPathsForRemote } from "./vbash.js";
|
|
12
12
|
import { decideCommand, decideFs, decideModel, decideNetwork, decideTool, normalizeMode } from "./authority.js";
|
|
13
13
|
import { K_BUILTINS } from "./shell/builtins/registry.js";
|
|
14
|
-
import { builtinErrorText, runBuiltin } from "./shell/builtins/run.js";
|
|
14
|
+
import { builtinCommandSpec, builtinErrorText, runBuiltin } from "./shell/builtins/run.js";
|
|
15
15
|
import { globMatcher } from "./shell/builtins/path.js";
|
|
16
16
|
import { commandHelpText, commandResultToShellResult, isShellAssignmentToken, isShellIdentifier, parseShellRedirects, quoteShellWord, recordStringMap, shellArgsToInput, shellExitCode, shellWordToken } from "./shell/runtime.js";
|
|
17
17
|
import { expandBraceFields, ShellExpander } from "./shell/expansion.js";
|
|
18
18
|
import { parseShellControlProgram } from "./shell/control.js";
|
|
19
19
|
import { parseShellFunctionProgram } from "./shell/functions.js";
|
|
20
20
|
import { hardenShellSandboxRequest, validateShellSandboxRequest } from "./sandbox.js";
|
|
21
|
+
import { isReadonlyRemoteCommand } from "./remote.js";
|
|
21
22
|
import { SessionMessages } from "./agent/messages.js";
|
|
22
23
|
import { SessionApprovals, normalizeApprovalDecision } from "./agent/approvals.js";
|
|
23
24
|
import { sealInterruptedTurns, sealUnfinishedToolEvents, sealUnfinishedTools } from "./agent/recovery.js";
|
|
@@ -394,8 +395,20 @@ export class AgentSession {
|
|
|
394
395
|
const op = stringField(current, 'op');
|
|
395
396
|
const path = stringField(current, 'path');
|
|
396
397
|
const decision = decideFs({ mode: this.modeState, op, path });
|
|
397
|
-
if (!decision.ok)
|
|
398
|
-
|
|
398
|
+
if (!decision.ok) {
|
|
399
|
+
if (this.modeState.name === 'plan') {
|
|
400
|
+
const approval = await this.requestAuthorityApproval('fs.before', current, {
|
|
401
|
+
message: `Plan mode requires approval before ${op} ${path}`,
|
|
402
|
+
risk: 'medium',
|
|
403
|
+
preview: { op, path },
|
|
404
|
+
});
|
|
405
|
+
if (approval.status === 'waiting_approval')
|
|
406
|
+
throw new Error(`waiting approval: ${approval.approval.id}`);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
throw new Error(decision.reason);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
399
412
|
return current;
|
|
400
413
|
}
|
|
401
414
|
async trace(type, data = {}) {
|
|
@@ -518,6 +531,20 @@ export class AgentSession {
|
|
|
518
531
|
}
|
|
519
532
|
return { rejected: false, current: value };
|
|
520
533
|
}
|
|
534
|
+
async requestAuthorityApproval(type, current, request) {
|
|
535
|
+
const remembered = await this.approvalLog.forAction(type, current);
|
|
536
|
+
if (remembered?.status === 'allowed') {
|
|
537
|
+
await this.consumeApproval(remembered);
|
|
538
|
+
return { status: 'allowed' };
|
|
539
|
+
}
|
|
540
|
+
if (remembered?.status === 'pending') {
|
|
541
|
+
await this.emitApprovalRequest(remembered);
|
|
542
|
+
return { status: 'waiting_approval', approval: remembered };
|
|
543
|
+
}
|
|
544
|
+
const approval = await this.approvalLog.create(type, current, request);
|
|
545
|
+
await this.emitApprovalRequest(approval);
|
|
546
|
+
return { status: 'waiting_approval', approval };
|
|
547
|
+
}
|
|
521
548
|
hookContext(type, current) {
|
|
522
549
|
return {
|
|
523
550
|
type,
|
|
@@ -537,10 +564,12 @@ export class AgentSession {
|
|
|
537
564
|
}
|
|
538
565
|
async emitApprovalRequest(approval) {
|
|
539
566
|
const request = approval.request || {};
|
|
567
|
+
const opId = stringField(approval.current || {}, 'execToolUseId');
|
|
540
568
|
await this.emit({
|
|
541
569
|
type: 'approval',
|
|
542
570
|
status: 'requested',
|
|
543
571
|
approvalId: approval.id,
|
|
572
|
+
opId: opId || undefined,
|
|
544
573
|
hook: approval.hook,
|
|
545
574
|
message: typeof request.message === 'string' ? request.message : 'Approval required',
|
|
546
575
|
risk: request.risk === 'low' || request.risk === 'medium' || request.risk === 'high' ? request.risk : undefined,
|
|
@@ -569,10 +598,13 @@ export class AgentSession {
|
|
|
569
598
|
approvalId,
|
|
570
599
|
hook: approval.hook,
|
|
571
600
|
decision: status === 'allowed' ? 'approved' : 'rejected',
|
|
601
|
+
opId: stringField(approval.current || {}, 'execToolUseId') || undefined,
|
|
572
602
|
approval,
|
|
573
603
|
});
|
|
574
|
-
if (status === 'rejected')
|
|
604
|
+
if (status === 'rejected') {
|
|
605
|
+
await this.rejectPausedApprovalTool(approval, row);
|
|
575
606
|
return row;
|
|
607
|
+
}
|
|
576
608
|
const result = await this.resumeApproval({ ...approval, status });
|
|
577
609
|
const done = { ...row, result };
|
|
578
610
|
await this.approvalLog.resumed(approvalId, result);
|
|
@@ -594,11 +626,15 @@ export class AgentSession {
|
|
|
594
626
|
return this.send(current.content, recordField(current, 'options') || {});
|
|
595
627
|
}
|
|
596
628
|
if (hook === 'shell.before' || hook === 'cmd.before') {
|
|
597
|
-
const
|
|
629
|
+
const execToolUseId = stringField(current, 'execToolUseId');
|
|
630
|
+
const resumeShell = () => this.execInternal(stringField(current, 'shellScript') || stringField(current, 'script'), {
|
|
598
631
|
by: stringField(current, 'by') || `approval:${approval.id}`,
|
|
599
632
|
foreground: typeof current.foreground === 'boolean' ? current.foreground : undefined,
|
|
633
|
+
execToolUseId,
|
|
600
634
|
});
|
|
601
|
-
const
|
|
635
|
+
const result = this.modeState.name === 'plan'
|
|
636
|
+
? await this.withTemporaryMode('normal', resumeShell)
|
|
637
|
+
: await resumeShell();
|
|
602
638
|
if (execToolUseId) {
|
|
603
639
|
const bounded = this.boundShellResult(result);
|
|
604
640
|
await this.appendExecToolExchange(execToolUseMessageFromApproval(current, execToolUseId), execToolUseId, bounded, stringField(current, 'by') || `approval:${approval.id}`);
|
|
@@ -614,9 +650,12 @@ export class AgentSession {
|
|
|
614
650
|
return result;
|
|
615
651
|
}
|
|
616
652
|
if (hook === 'tool.before') {
|
|
617
|
-
|
|
653
|
+
const resumeTool = () => this.command(stringField(current, 'command'), recordField(current, 'input') || {}, {
|
|
618
654
|
by: stringField(current, 'by') || `approval:${approval.id}`,
|
|
619
655
|
});
|
|
656
|
+
return this.modeState.name === 'plan'
|
|
657
|
+
? this.withTemporaryMode('normal', resumeTool)
|
|
658
|
+
: resumeTool();
|
|
620
659
|
}
|
|
621
660
|
if (hook === 'tool.after') {
|
|
622
661
|
await this.trace('tool.ended', { name: current.command, resultType: resultType(current.result), approvedBy: approval.id });
|
|
@@ -655,6 +694,30 @@ export class AgentSession {
|
|
|
655
694
|
}
|
|
656
695
|
throw new Error(`approval hook is not resumable: ${hook}`);
|
|
657
696
|
}
|
|
697
|
+
async rejectPausedApprovalTool(approval, row) {
|
|
698
|
+
const current = approval.current || {};
|
|
699
|
+
const execToolUseId = stringField(current, 'execToolUseId');
|
|
700
|
+
if (!execToolUseId)
|
|
701
|
+
return;
|
|
702
|
+
const by = stringField(row, 'by') || 'approval';
|
|
703
|
+
const result = this.boundShellResult({
|
|
704
|
+
status: 'denied',
|
|
705
|
+
code: 126,
|
|
706
|
+
stdout: '',
|
|
707
|
+
stderr: `Approval rejected: ${approval.id}`,
|
|
708
|
+
approvalId: approval.id,
|
|
709
|
+
});
|
|
710
|
+
await this.appendExecToolExchange(execToolUseMessageFromApproval(current, execToolUseId), execToolUseId, result, by);
|
|
711
|
+
await this.trace('tool.denied', { toolUseId: execToolUseId, approvalId: approval.id, hook: approval.hook });
|
|
712
|
+
await this.emit({
|
|
713
|
+
type: 'tool',
|
|
714
|
+
status: 'denied',
|
|
715
|
+
tool: 'shell',
|
|
716
|
+
result,
|
|
717
|
+
opId: execToolUseId,
|
|
718
|
+
approvalId: approval.id,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
658
721
|
async resumeFsApproval(current) {
|
|
659
722
|
const op = stringField(current, 'op');
|
|
660
723
|
const path = stringField(current, 'path');
|
|
@@ -1054,6 +1117,15 @@ export class AgentSession {
|
|
|
1054
1117
|
shellFunctions: Object.keys(this.shellFunctions).length,
|
|
1055
1118
|
};
|
|
1056
1119
|
}
|
|
1120
|
+
isReadonlyCommandName(cmd, args = [], options = {}) {
|
|
1121
|
+
if (!options.forceRemote && K_BUILTINS.has(cmd))
|
|
1122
|
+
return builtinCommandSpec(cmd)?.readonly === true;
|
|
1123
|
+
const command = !options.forceRemote ? this.runtime.commands.get(cmd) : undefined;
|
|
1124
|
+
if (command)
|
|
1125
|
+
return command.readonly === true;
|
|
1126
|
+
const remote = options.remote ? this.runtime.getRemote(options.remote) : this.runtime.defaultRemote();
|
|
1127
|
+
return isReadonlyRemoteCommand(remote, cmd, args);
|
|
1128
|
+
}
|
|
1057
1129
|
async exec(command, options = {}) {
|
|
1058
1130
|
const toolUseId = id('tool');
|
|
1059
1131
|
const by = normalizeBy(options.by);
|
|
@@ -1233,10 +1305,41 @@ export class AgentSession {
|
|
|
1233
1305
|
wordTokens: preservesTokens ? concrete.wordTokens : words.map(shellWordToken),
|
|
1234
1306
|
background: concrete.background,
|
|
1235
1307
|
};
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1308
|
+
if (!options.skipCommandAuthority) {
|
|
1309
|
+
const authority = decideCommand({
|
|
1310
|
+
mode: this.modeState,
|
|
1311
|
+
cmd: currentCmd,
|
|
1312
|
+
readonly: this.isReadonlyCommandName(currentCmd, currentArgs, { forceRemote: options.forceRemote, remote: options.remote }),
|
|
1313
|
+
});
|
|
1314
|
+
if (!authority.ok) {
|
|
1315
|
+
if (this.modeState.name === 'plan') {
|
|
1316
|
+
const current = {
|
|
1317
|
+
...beforeCmd.current,
|
|
1318
|
+
cmd: currentCmd,
|
|
1319
|
+
args: currentArgs,
|
|
1320
|
+
script: currentScript,
|
|
1321
|
+
shellScript: script,
|
|
1322
|
+
cwd: this.hookCwd(),
|
|
1323
|
+
by: normalizeBy(options.by),
|
|
1324
|
+
foreground: foreground && !concrete.background,
|
|
1325
|
+
background: !foreground || concrete.background,
|
|
1326
|
+
execToolUseId: options.execToolUseId,
|
|
1327
|
+
};
|
|
1328
|
+
const approval = await this.requestAuthorityApproval('cmd.before', current, {
|
|
1329
|
+
message: `Plan mode requires approval before command ${currentCmd}`,
|
|
1330
|
+
risk: 'medium',
|
|
1331
|
+
preview: { cmd: currentCmd, args: currentArgs, cwd: this.hookCwd() },
|
|
1332
|
+
});
|
|
1333
|
+
if (approval.status === 'waiting_approval') {
|
|
1334
|
+
await this.trace('process.waiting_approval', { cmd: currentCmd, args: currentArgs, reason: authority.reason, approvalId: approval.approval.id, mode: this.mode });
|
|
1335
|
+
return { status: 'waiting_approval', code: undefined, stdout: '', stderr: '', approval: approval.approval, reason: authority.reason };
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
else {
|
|
1339
|
+
await this.trace('process.rejected', { cmd: currentCmd, args: currentArgs, reason: authority.reason, mode: this.mode });
|
|
1340
|
+
return { status: 'rejected', code: 126, stdout: '', stderr: authority.reason };
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1240
1343
|
}
|
|
1241
1344
|
}
|
|
1242
1345
|
const cmd = parsed[0]?.cmd || 'bash';
|
|
@@ -2659,10 +2762,28 @@ export class AgentSession {
|
|
|
2659
2762
|
throw new Error(before.reason || `tool.before rejected ${toolName}`);
|
|
2660
2763
|
}
|
|
2661
2764
|
const currentInput = recordField(before.current, 'input') || input;
|
|
2662
|
-
const authority = decideTool({
|
|
2765
|
+
const authority = decideTool({
|
|
2766
|
+
mode: this.modeState,
|
|
2767
|
+
tool: toolName,
|
|
2768
|
+
command: name,
|
|
2769
|
+
readonly: command.readonly === true,
|
|
2770
|
+
});
|
|
2663
2771
|
if (!authority.ok) {
|
|
2664
|
-
|
|
2665
|
-
|
|
2772
|
+
if (this.modeState.name === 'plan') {
|
|
2773
|
+
const approval = await this.requestAuthorityApproval('tool.before', { tool: toolName, command: name, input: currentInput, by }, {
|
|
2774
|
+
message: `Plan mode requires approval before tool ${toolName}`,
|
|
2775
|
+
risk: 'medium',
|
|
2776
|
+
preview: { tool: toolName, input: currentInput },
|
|
2777
|
+
});
|
|
2778
|
+
if (approval.status === 'waiting_approval') {
|
|
2779
|
+
await this.trace('tool.waiting_approval', { name, input: currentInput, by, reason: authority.reason, approvalId: approval.approval.id, mode: this.mode });
|
|
2780
|
+
return { status: 'waiting_approval', approval: approval.approval, reason: authority.reason };
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
else {
|
|
2784
|
+
await this.trace('tool.rejected', { name, input: currentInput, by, reason: authority.reason, mode: this.mode });
|
|
2785
|
+
throw new Error(authority.reason);
|
|
2786
|
+
}
|
|
2666
2787
|
}
|
|
2667
2788
|
const toolUseId = id('tool');
|
|
2668
2789
|
const parsed = parseCommandInput(name, command, currentInput);
|
|
@@ -2685,7 +2806,7 @@ export class AgentSession {
|
|
|
2685
2806
|
: typeof command.mode === 'string'
|
|
2686
2807
|
? command.mode
|
|
2687
2808
|
: undefined;
|
|
2688
|
-
const runShell = () => this.withTemporaryShellState({ env: positionalShellEnv(name, argv) }, () => this.execInternal(String(command.script ?? ''), { by: options.by || `command:${name}
|
|
2809
|
+
const runShell = () => this.withTemporaryShellState({ env: positionalShellEnv(name, argv) }, () => this.execInternal(String(command.script ?? ''), { by: options.by || `command:${name}`, skipCommandAuthority: command.readonly === true }));
|
|
2689
2810
|
result = invocationMode ? await this.withTemporaryMode(invocationMode, runShell) : await runShell();
|
|
2690
2811
|
}
|
|
2691
2812
|
else if (command.kind === 'markdown') {
|