@awak-app/simy-cli 0.2.3 → 0.4.0
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/README.md +145 -1
- package/package.json +14 -5
- package/src/agent.js +740 -37
- package/src/bounded-local-task-subtask-pool.js +708 -0
- package/src/bounded-local-task-subtasks-contract.js +1189 -0
- package/src/cli-contract.js +42 -3
- package/src/console/app.js +212 -90
- package/src/desktop-executor.js +21 -2
- package/src/durable-local-task-steps-contract.js +1256 -0
- package/src/durable-local-task-worker.js +2607 -0
- package/src/execution-capability-contract.js +116 -0
- package/src/execution-guardrail.js +69 -12
- package/src/index.js +22 -7
- package/src/local-attachments.js +94 -113
- package/src/local-task-artifact-contract.js +266 -0
- package/src/local-task-attachment-store.js +447 -0
- package/src/local-task-file-capabilities.js +738 -0
- package/src/local-task-scenario-packs.js +681 -0
- package/src/local-task.js +1137 -0
- package/src/orchestrator/audit.js +42 -1
- package/src/orchestrator/loop.js +29 -3
- package/src/repository-inventory.js +37 -1
- package/src/runner.js +44 -0
- package/src/shutdown.js +63 -0
- package/src/sqm/bundle-store.js +249 -0
- package/src/sqm/canonical.js +45 -0
- package/src/sqm/checkers.js +299 -0
- package/src/sqm/command.js +149 -0
- package/src/sqm/evidence-client.js +12 -0
- package/src/sqm/index.js +141 -0
- package/src/sqm/proof.js +154 -0
- package/src/sqm/repository.js +163 -0
- package/src/sqm/session.js +58 -0
- package/src/sqm/validation.js +305 -0
- package/src/workspace-context.js +40 -7
package/src/cli-contract.js
CHANGED
|
@@ -1,11 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { localTaskFileCapabilityMetadata } from "./local-task-file-capabilities.js";
|
|
2
|
+
import { localTaskScenarioPackMetadata } from "./local-task-scenario-packs.js";
|
|
3
|
+
import {
|
|
4
|
+
boundedLocalTaskSubtasksCapabilityMetadata,
|
|
5
|
+
} from "./bounded-local-task-subtasks-contract.js";
|
|
6
|
+
import {
|
|
7
|
+
DURABLE_LOCAL_TASK_STEP_CLAIM_SCHEMA,
|
|
8
|
+
LOCAL_TASK_PLAN_ORCHESTRATION_SCHEMA,
|
|
9
|
+
LOCAL_TASK_PLAN_SCHEMA,
|
|
10
|
+
LOCAL_TASK_STEP_CONTEXT_SCHEMA,
|
|
11
|
+
LOCAL_TASK_STEP_SCHEMA,
|
|
12
|
+
MAX_LOCAL_TASK_PLAN_STEPS,
|
|
13
|
+
} from "./durable-local-task-steps-contract.js";
|
|
14
|
+
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
3
15
|
|
|
4
|
-
export
|
|
16
|
+
export const CLI_VERSION = "0.4.0";
|
|
17
|
+
export const CLI_API_CONTRACT_VERSION = 10;
|
|
18
|
+
|
|
19
|
+
export function withCliContract(
|
|
20
|
+
capabilities = {},
|
|
21
|
+
installMode = null,
|
|
22
|
+
{ authenticated = false } = {},
|
|
23
|
+
) {
|
|
5
24
|
return {
|
|
6
25
|
...capabilities,
|
|
7
26
|
cli_version: CLI_VERSION,
|
|
8
27
|
api_contract_version: CLI_API_CONTRACT_VERSION,
|
|
28
|
+
local_task_file_capabilities: localTaskFileCapabilityMetadata(),
|
|
29
|
+
local_task_scenario_packs: localTaskScenarioPackMetadata(),
|
|
30
|
+
bounded_local_task_subtasks:
|
|
31
|
+
boundedLocalTaskSubtasksCapabilityMetadata(),
|
|
32
|
+
durable_local_task_steps: {
|
|
33
|
+
orchestration_schema_version: LOCAL_TASK_PLAN_ORCHESTRATION_SCHEMA,
|
|
34
|
+
claim_schema_version: DURABLE_LOCAL_TASK_STEP_CLAIM_SCHEMA,
|
|
35
|
+
plan_schema_version: LOCAL_TASK_PLAN_SCHEMA,
|
|
36
|
+
step_schema_version: LOCAL_TASK_STEP_SCHEMA,
|
|
37
|
+
context_schema_version: LOCAL_TASK_STEP_CONTEXT_SCHEMA,
|
|
38
|
+
max_steps: MAX_LOCAL_TASK_PLAN_STEPS,
|
|
39
|
+
},
|
|
40
|
+
execution_capabilities: buildExecutionCapabilityContract({
|
|
41
|
+
cliVersion: CLI_VERSION,
|
|
42
|
+
apiContractVersion: CLI_API_CONTRACT_VERSION,
|
|
43
|
+
installMode,
|
|
44
|
+
backends: capabilities.backends,
|
|
45
|
+
backendVersions: capabilities.backend_versions,
|
|
46
|
+
authenticated,
|
|
47
|
+
}),
|
|
9
48
|
...(installMode ? { install_mode: installMode } : {}),
|
|
10
49
|
};
|
|
11
50
|
}
|
package/src/console/app.js
CHANGED
|
@@ -68,14 +68,12 @@ export function AgenticLoopConsole({ agent, onQuit = () => {} }) {
|
|
|
68
68
|
const selectedRun = runs.find((run) => run.id === selectedId) ?? null;
|
|
69
69
|
const selectableIds = [NEW_TASK, ...runs.map((run) => run.id)];
|
|
70
70
|
const selectedIndex = Math.max(0, selectableIds.indexOf(selectedId));
|
|
71
|
-
const
|
|
72
|
-
selectedRun &&
|
|
73
|
-
(selectedRun.status === "waiting_human" || selectedRun.controlState === "waiting_human"),
|
|
71
|
+
const hasNextAction = Boolean(
|
|
72
|
+
selectedRun && nextActionForRun(selectedRun, isRepositoryMissing(selectedRun)),
|
|
74
73
|
);
|
|
75
74
|
const actionRows =
|
|
76
75
|
1 +
|
|
77
|
-
(
|
|
78
|
-
(!selectedRun ? 1 : 0) +
|
|
76
|
+
(hasNextAction ? 1 : 0) +
|
|
79
77
|
(notice || busy ? 1 : 0) +
|
|
80
78
|
(visibleUpdateNotice(cliUpdate) ? 1 : 0);
|
|
81
79
|
const mainHeight = Math.max(8, terminal.rows - 5 - actionRows);
|
|
@@ -486,7 +484,7 @@ export function AgenticLoopConsole({ agent, onQuit = () => {} }) {
|
|
|
486
484
|
flexDirection: "column",
|
|
487
485
|
width: terminal.columns,
|
|
488
486
|
children: [
|
|
489
|
-
jsx(Header, { agent, runCount: runs.length }),
|
|
487
|
+
jsx(Header, { agent, runCount: runs.length, width: terminal.columns }),
|
|
490
488
|
jsx(Box, {
|
|
491
489
|
marginTop: 1,
|
|
492
490
|
height: mainHeight,
|
|
@@ -535,10 +533,10 @@ export function AgenticLoopConsole({ agent, onQuit = () => {} }) {
|
|
|
535
533
|
busy,
|
|
536
534
|
notice,
|
|
537
535
|
confirmStop,
|
|
538
|
-
draft,
|
|
539
536
|
repositoryMissing: isRepositoryMissing(selectedRun),
|
|
540
537
|
modalOpen: Boolean(scanPrompt || repositoryPicker),
|
|
541
538
|
cliUpdate,
|
|
539
|
+
width: terminal.columns,
|
|
542
540
|
}),
|
|
543
541
|
],
|
|
544
542
|
});
|
|
@@ -546,48 +544,77 @@ export function AgenticLoopConsole({ agent, onQuit = () => {} }) {
|
|
|
546
544
|
|
|
547
545
|
export const CodingLoopConsole = AgenticLoopConsole;
|
|
548
546
|
|
|
549
|
-
function Header({ agent, runCount }) {
|
|
547
|
+
function Header({ agent, runCount, width }) {
|
|
548
|
+
const compact = width < 84;
|
|
549
|
+
const version = agent.updates?.snapshot?.().current_version;
|
|
550
550
|
return jsxs(Box, {
|
|
551
551
|
borderStyle: "single",
|
|
552
552
|
borderColor: "cyan",
|
|
553
553
|
paddingX: 1,
|
|
554
|
+
flexDirection: compact ? "column" : "row",
|
|
554
555
|
children: [
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
556
|
+
jsxs(Box, {
|
|
557
|
+
children: [
|
|
558
|
+
jsx(Text, { bold: true, color: "cyan", children: "SIMY" }),
|
|
559
|
+
jsx(Text, { bold: true, children: " Local Agent" }),
|
|
560
|
+
version ? jsx(Text, { dimColor: true, children: ` v${version}` }) : null,
|
|
561
|
+
],
|
|
562
|
+
}),
|
|
563
|
+
compact
|
|
564
|
+
? jsxs(Text, {
|
|
559
565
|
dimColor: true,
|
|
560
|
-
children:
|
|
566
|
+
children: [
|
|
567
|
+
`Connected · localhost:${agent.port}`,
|
|
568
|
+
jsx(Text, { children: ` · ${runCount} ${runCount === 1 ? "run" : "runs"}` }),
|
|
569
|
+
],
|
|
561
570
|
})
|
|
562
|
-
:
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
571
|
+
: jsx(Text, { dimColor: true, children: ` Connected · localhost:${agent.port}` }),
|
|
572
|
+
compact ? null : jsx(Spacer, {}),
|
|
573
|
+
compact
|
|
574
|
+
? null
|
|
575
|
+
: jsx(Text, {
|
|
576
|
+
color: runCount > 0 ? "green" : undefined,
|
|
577
|
+
children: `${runCount} ${runCount === 1 ? "run" : "runs"}`,
|
|
578
|
+
}),
|
|
579
|
+
compact ? null : jsx(Text, { dimColor: true, children: ` · ${agent.webOrigin}` }),
|
|
567
580
|
],
|
|
568
581
|
});
|
|
569
582
|
}
|
|
570
583
|
|
|
571
584
|
function NewTask({ draft, loginUrl, width }) {
|
|
585
|
+
const compact = width < 72;
|
|
572
586
|
return jsxs(Box, {
|
|
573
587
|
width,
|
|
588
|
+
alignSelf: "flex-start",
|
|
574
589
|
flexDirection: "column",
|
|
575
590
|
borderStyle: "single",
|
|
576
591
|
borderColor: "cyan",
|
|
577
592
|
paddingX: 1,
|
|
578
593
|
children: [
|
|
579
|
-
jsx(Text, { bold: true, color: "cyan", children: "
|
|
594
|
+
jsx(Text, { bold: true, color: "cyan", children: "NEW TASK" }),
|
|
595
|
+
jsx(Text, { bold: true, children: "What should SIMY do?" }),
|
|
580
596
|
jsx(Text, {
|
|
581
597
|
dimColor: true,
|
|
582
|
-
|
|
598
|
+
wrap: "wrap",
|
|
599
|
+
children:
|
|
600
|
+
"Describe the outcome. SIMY will choose a one-time Local Task or an Agentic Loop.",
|
|
601
|
+
}),
|
|
602
|
+
jsx(Text, { children: " " }),
|
|
603
|
+
jsx(Text, {
|
|
604
|
+
children: `${compact ? "Repo" : "Repository"} ${draft.repository || "Not selected"}`,
|
|
583
605
|
}),
|
|
584
|
-
jsx(Text, { children: `Repository ${draft.repository || "not set"}` }),
|
|
585
606
|
jsx(Text, { children: `Branch ${draft.branch}` }),
|
|
586
|
-
jsx(Text, { children: `
|
|
607
|
+
jsx(Text, { children: `Agent ${executorLabel(draft.backend)}` }),
|
|
587
608
|
jsx(Text, {
|
|
588
|
-
|
|
609
|
+
wrap: "wrap",
|
|
610
|
+
children: `Files ${draft.attachmentPaths.length === 0 ? "None" : draft.attachmentPaths.join(", ")}`,
|
|
611
|
+
}),
|
|
612
|
+
jsx(Text, { children: " " }),
|
|
613
|
+
jsx(Text, {
|
|
614
|
+
bold: true,
|
|
615
|
+
children: "Next Type your request below, then press Enter.",
|
|
589
616
|
}),
|
|
590
|
-
loginUrl ? jsx(Text, { color: "cyan", children: `Sign in
|
|
617
|
+
loginUrl ? jsx(Text, { color: "cyan", wrap: "wrap", children: `Sign in ${loginUrl}` }) : null,
|
|
591
618
|
],
|
|
592
619
|
});
|
|
593
620
|
}
|
|
@@ -601,9 +628,10 @@ function RepositoryScanConsent({ root, width, height }) {
|
|
|
601
628
|
borderColor: "yellow",
|
|
602
629
|
paddingX: 1,
|
|
603
630
|
children: [
|
|
604
|
-
jsx(Text, { bold: true, color: "yellow", children: "
|
|
631
|
+
jsx(Text, { bold: true, color: "yellow", children: "REPOSITORY ACCESS" }),
|
|
632
|
+
jsx(Text, { bold: true, children: "Allow SIMY to find local Git repositories?" }),
|
|
605
633
|
jsx(Text, { children: " " }),
|
|
606
|
-
jsx(Text, {
|
|
634
|
+
jsx(Text, { dimColor: true, children: "Folder to scan" }),
|
|
607
635
|
jsx(Text, { color: "cyan", wrap: "wrap", children: root }),
|
|
608
636
|
jsx(Text, { children: " " }),
|
|
609
637
|
jsx(Text, {
|
|
@@ -617,8 +645,12 @@ function RepositoryScanConsent({ root, width, height }) {
|
|
|
617
645
|
"Source files and file contents are not indexed or uploaded. Hidden, dependency, cache, and symlink directories are skipped.",
|
|
618
646
|
}),
|
|
619
647
|
jsx(Text, { children: " " }),
|
|
620
|
-
jsx(Text, { bold: true,
|
|
621
|
-
jsx(Text, {
|
|
648
|
+
jsx(Text, { bold: true, children: "Next Press y to allow this scan." }),
|
|
649
|
+
jsx(Text, {
|
|
650
|
+
dimColor: true,
|
|
651
|
+
wrap: "wrap",
|
|
652
|
+
children: "Press n or Esc to cancel. Use /scan <path> to choose a narrower folder.",
|
|
653
|
+
}),
|
|
622
654
|
],
|
|
623
655
|
});
|
|
624
656
|
}
|
|
@@ -640,7 +672,7 @@ function RepositoryPicker({ repositories, selected, requiredRepository, width, h
|
|
|
640
672
|
children: [
|
|
641
673
|
jsxs(Box, {
|
|
642
674
|
children: [
|
|
643
|
-
jsx(Text, { bold: true, color: "cyan", children: "
|
|
675
|
+
jsx(Text, { bold: true, color: "cyan", children: "CHOOSE A REPOSITORY" }),
|
|
644
676
|
jsx(Spacer, {}),
|
|
645
677
|
jsx(Text, { dimColor: true, children: `${repositories.length} indexed` }),
|
|
646
678
|
],
|
|
@@ -650,7 +682,7 @@ function RepositoryPicker({ repositories, selected, requiredRepository, width, h
|
|
|
650
682
|
color: "yellow",
|
|
651
683
|
children: `This run requires ${requiredRepository}`,
|
|
652
684
|
})
|
|
653
|
-
: jsx(Text, { dimColor: true, children: "Choose the
|
|
685
|
+
: jsx(Text, { dimColor: true, children: "Choose the workspace for the next task." }),
|
|
654
686
|
...visible.map((repository, offset) => {
|
|
655
687
|
const index = start + offset;
|
|
656
688
|
const active = index === selected;
|
|
@@ -672,12 +704,13 @@ function RepositoryPicker({ repositories, selected, requiredRepository, width, h
|
|
|
672
704
|
],
|
|
673
705
|
}, repository.local_path);
|
|
674
706
|
}),
|
|
675
|
-
jsx(Text, { dimColor: true, children: "
|
|
707
|
+
jsx(Text, { dimColor: true, children: "↑/↓ select Enter choose Esc cancel" }),
|
|
676
708
|
],
|
|
677
709
|
});
|
|
678
710
|
}
|
|
679
711
|
|
|
680
712
|
function RunList({ runs, selectedId, width, height }) {
|
|
713
|
+
const compact = width < 84;
|
|
681
714
|
return jsxs(Box, {
|
|
682
715
|
width,
|
|
683
716
|
height,
|
|
@@ -690,33 +723,37 @@ function RunList({ runs, selectedId, width, height }) {
|
|
|
690
723
|
children: [
|
|
691
724
|
jsx(Text, { bold: true, children: "RUNS" }),
|
|
692
725
|
jsx(Spacer, {}),
|
|
693
|
-
jsx(Text, { dimColor: true, children: "Enter
|
|
726
|
+
jsx(Text, { dimColor: true, children: compact ? "Enter open" : "Enter opens details" }),
|
|
694
727
|
],
|
|
695
728
|
}),
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
children: [
|
|
701
|
-
jsx(Text, { color: "cyan", bold: selectedId === NEW_TASK, children: "+ New task" }),
|
|
702
|
-
],
|
|
729
|
+
jsx(Text, {
|
|
730
|
+
color: "cyan",
|
|
731
|
+
bold: selectedId === NEW_TASK,
|
|
732
|
+
children: `${selectedId === NEW_TASK ? "> " : " "}+ New task`,
|
|
703
733
|
}),
|
|
704
734
|
...runs.slice(0, 10).map((run) => {
|
|
705
735
|
const selected = run.id === selectedId;
|
|
706
736
|
return jsxs(Box, {
|
|
707
737
|
flexDirection: "column",
|
|
708
|
-
|
|
709
|
-
borderColor: selected ? "cyan" : undefined,
|
|
710
|
-
paddingX: selected ? 1 : 2,
|
|
738
|
+
paddingLeft: 0,
|
|
711
739
|
children: [
|
|
712
740
|
jsxs(Text, {
|
|
713
741
|
children: [
|
|
742
|
+
jsx(Text, { color: selected ? "cyan" : undefined, children: selected ? "> " : " " }),
|
|
714
743
|
jsx(Text, { color: statusColor(run), children: `${statusGlyph(run)} ` }),
|
|
715
744
|
jsx(Text, { bold: selected, children: shortId(run.id) }),
|
|
716
|
-
jsx(Text, {
|
|
745
|
+
jsx(Text, {
|
|
746
|
+
dimColor: true,
|
|
747
|
+
children: compact
|
|
748
|
+
? ` ${statusLabel(run)}`
|
|
749
|
+
: ` ${statusLabel(run)} · ${executorLabel(run.request.backend)}`,
|
|
750
|
+
}),
|
|
717
751
|
],
|
|
718
752
|
}),
|
|
719
|
-
jsx(Text, {
|
|
753
|
+
jsx(Text, {
|
|
754
|
+
dimColor: true,
|
|
755
|
+
children: ` ${truncate(run.request.requirement, Math.max(12, width - 8))}`,
|
|
756
|
+
}),
|
|
720
757
|
],
|
|
721
758
|
}, run.id);
|
|
722
759
|
}),
|
|
@@ -744,8 +781,9 @@ function RunDetail({ run, rows, scrollFromEnd, width, height }) {
|
|
|
744
781
|
children: [
|
|
745
782
|
jsxs(Text, {
|
|
746
783
|
children: [
|
|
747
|
-
jsx(Text, {
|
|
748
|
-
jsx(Text, {
|
|
784
|
+
jsx(Text, { color: statusColor(run), children: `${statusGlyph(run)} ` }),
|
|
785
|
+
jsx(Text, { bold: true, children: statusLabel(run) }),
|
|
786
|
+
jsx(Text, { dimColor: true, children: ` ${shortId(run.id)}` }),
|
|
749
787
|
],
|
|
750
788
|
}),
|
|
751
789
|
jsx(Spacer, {}),
|
|
@@ -755,7 +793,7 @@ function RunDetail({ run, rows, scrollFromEnd, width, height }) {
|
|
|
755
793
|
}),
|
|
756
794
|
jsx(Text, {
|
|
757
795
|
color: scroll > 0 ? "yellow" : run.child ? "green" : "gray",
|
|
758
|
-
children: scroll > 0 ? "
|
|
796
|
+
children: scroll > 0 ? " ‖ history" : run.child ? " ● live" : " ○ idle",
|
|
759
797
|
}),
|
|
760
798
|
],
|
|
761
799
|
}),
|
|
@@ -772,6 +810,10 @@ function RunDetail({ run, rows, scrollFromEnd, width, height }) {
|
|
|
772
810
|
}
|
|
773
811
|
|
|
774
812
|
function Help({ width }) {
|
|
813
|
+
const groups = [
|
|
814
|
+
["TASK", consoleHelpLines().slice(0, 8)],
|
|
815
|
+
["RUN", consoleHelpLines().slice(8, 15)],
|
|
816
|
+
];
|
|
775
817
|
return jsxs(Box, {
|
|
776
818
|
width,
|
|
777
819
|
borderStyle: "single",
|
|
@@ -779,8 +821,12 @@ function Help({ width }) {
|
|
|
779
821
|
paddingX: 1,
|
|
780
822
|
flexDirection: "column",
|
|
781
823
|
children: [
|
|
782
|
-
jsx(Text, { bold: true, color: "blue", children: "
|
|
783
|
-
...
|
|
824
|
+
jsx(Text, { bold: true, color: "blue", children: "HELP" }),
|
|
825
|
+
...groups.flatMap(([label, lines]) => [
|
|
826
|
+
jsx(Text, { bold: true, children: label }, `${label}-heading`),
|
|
827
|
+
...lines.map((line) => jsx(Text, { children: line }, line)),
|
|
828
|
+
]),
|
|
829
|
+
jsx(Text, { dimColor: true, children: "Esc closes help" }),
|
|
784
830
|
],
|
|
785
831
|
});
|
|
786
832
|
}
|
|
@@ -793,23 +839,23 @@ function ActionBar({
|
|
|
793
839
|
busy,
|
|
794
840
|
notice,
|
|
795
841
|
confirmStop,
|
|
796
|
-
draft,
|
|
797
842
|
repositoryMissing,
|
|
798
843
|
modalOpen,
|
|
799
844
|
cliUpdate,
|
|
845
|
+
width,
|
|
800
846
|
}) {
|
|
801
|
-
const
|
|
847
|
+
const compact = width < 84;
|
|
848
|
+
const nextAction = run ? nextActionForRun(run, repositoryMissing) : null;
|
|
802
849
|
return jsxs(Box, {
|
|
803
850
|
flexDirection: "column",
|
|
804
851
|
marginTop: 1,
|
|
805
852
|
children: [
|
|
806
|
-
|
|
853
|
+
nextAction && !modalOpen && !inputMode
|
|
807
854
|
? jsx(Text, {
|
|
808
855
|
bold: true,
|
|
809
|
-
color:
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
: "HUMAN INPUT REQUIRED Press i, type guidance, then Enter.",
|
|
856
|
+
color: nextAction.tone,
|
|
857
|
+
wrap: "wrap",
|
|
858
|
+
children: `NEXT ACTION ${nextAction.text}`,
|
|
813
859
|
})
|
|
814
860
|
: null,
|
|
815
861
|
modalOpen
|
|
@@ -825,27 +871,39 @@ function ActionBar({
|
|
|
825
871
|
: jsx(Text, {
|
|
826
872
|
dimColor: true,
|
|
827
873
|
children: detailOpen
|
|
828
|
-
?
|
|
829
|
-
|
|
874
|
+
? compact
|
|
875
|
+
? "↑/↓ scroll Esc runs i act ? help q quit"
|
|
876
|
+
: "↑/↓ scroll PgUp/PgDn page Home/End Esc runs i act / command ? help q quit"
|
|
877
|
+
: compact
|
|
878
|
+
? "↑/↓ select Enter open i act ? help q quit"
|
|
879
|
+
: "↑/↓ select Enter open i act / command s repositories ? help q quit",
|
|
830
880
|
}),
|
|
831
|
-
!run
|
|
832
|
-
? jsx(Text, {
|
|
833
|
-
dimColor: true,
|
|
834
|
-
children: `${draft.repository || "repo not set"} ${draft.branch} ${executorLabel(draft.backend)}`,
|
|
835
|
-
})
|
|
836
|
-
: null,
|
|
837
881
|
notice
|
|
838
882
|
? jsx(Text, {
|
|
839
883
|
color: confirmStop ? "yellow" : notice.includes("could not") || notice.includes("requires") ? "red" : "cyan",
|
|
840
|
-
|
|
884
|
+
wrap: "wrap",
|
|
885
|
+
children: `${busy ? "WORKING " : "STATUS "}${notice}`,
|
|
841
886
|
})
|
|
842
887
|
: busy
|
|
843
|
-
? jsx(Text, { color: "cyan", children: "
|
|
888
|
+
? jsx(Text, { color: "cyan", children: "WORKING Please wait…" })
|
|
844
889
|
: null,
|
|
845
890
|
visibleUpdateNotice(cliUpdate)
|
|
846
|
-
?
|
|
847
|
-
|
|
848
|
-
children:
|
|
891
|
+
? jsxs(Box, {
|
|
892
|
+
flexDirection: "column",
|
|
893
|
+
children: [
|
|
894
|
+
jsx(Text, {
|
|
895
|
+
bold: true,
|
|
896
|
+
color: cliUpdate.state === "failed" ? "red" : "yellow",
|
|
897
|
+
wrap: "wrap",
|
|
898
|
+
children: `UPDATE ${cliUpdate.message || "SIMY CLI update status changed."}`,
|
|
899
|
+
}),
|
|
900
|
+
cliUpdate.action?.command
|
|
901
|
+
? jsx(Text, {
|
|
902
|
+
wrap: "wrap",
|
|
903
|
+
children: `Next ${cliUpdate.action.command}`,
|
|
904
|
+
})
|
|
905
|
+
: null,
|
|
906
|
+
],
|
|
849
907
|
})
|
|
850
908
|
: null,
|
|
851
909
|
],
|
|
@@ -861,8 +919,7 @@ function visibleUpdateNotice(update) {
|
|
|
861
919
|
) {
|
|
862
920
|
return "";
|
|
863
921
|
}
|
|
864
|
-
|
|
865
|
-
return `${update.message || "SIMY CLI update status changed."}${command}`;
|
|
922
|
+
return update.message || "SIMY CLI update status changed.";
|
|
866
923
|
}
|
|
867
924
|
|
|
868
925
|
function initialDraft(agent) {
|
|
@@ -914,33 +971,40 @@ function buildRunDetailRows(run, width) {
|
|
|
914
971
|
for (const text of wrapTerminalText(value, width)) rows.push({ text, ...style });
|
|
915
972
|
};
|
|
916
973
|
|
|
974
|
+
const attemptNumber = run.snapshot.attempts.length + (run.child ? 1 : 0);
|
|
975
|
+
const maxAttempts = run.snapshot.charter?.max_attempts;
|
|
917
976
|
add(
|
|
918
|
-
|
|
977
|
+
`${executorLabel(run.request.backend)} · Attempt ${attemptNumber}${maxAttempts ? `/${maxAttempts}` : ""} · PID ${run.child?.pid ?? "—"}`,
|
|
919
978
|
{ dimColor: true },
|
|
920
979
|
);
|
|
921
|
-
add(
|
|
980
|
+
add(`${run.request.repository} · ${run.request.base_branch || run.snapshot.charter?.base_branch || "dev"}`, {
|
|
981
|
+
dimColor: true,
|
|
982
|
+
});
|
|
922
983
|
if (run.request.attachments?.length) {
|
|
923
|
-
add(`
|
|
984
|
+
add(`Files · ${run.request.attachments.map((item) => item.name).join(", ")}`, {
|
|
924
985
|
dimColor: true,
|
|
925
986
|
});
|
|
926
987
|
}
|
|
927
988
|
add();
|
|
928
|
-
add("
|
|
989
|
+
add("GOAL", { bold: true, color: "cyan" });
|
|
929
990
|
add(run.request.requirement);
|
|
930
|
-
add();
|
|
931
|
-
add("SIMY", { bold: true, color: "green" });
|
|
932
|
-
add(statusLabel(run), { color: statusColor(run) });
|
|
933
991
|
|
|
934
992
|
if (presentation) {
|
|
935
993
|
add();
|
|
936
|
-
add("STEP
|
|
994
|
+
add("CURRENT STEP", { bold: true, color: "cyan" });
|
|
937
995
|
add(presentation.summary);
|
|
938
|
-
if (presentation.result) add(`Result
|
|
939
|
-
|
|
996
|
+
if (presentation.result) add(`Result · ${presentation.result}`, { dimColor: true });
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
const nextAction = nextActionForRun(run, isRepositoryMissing(run));
|
|
1000
|
+
if (!nextAction && presentation?.next) {
|
|
1001
|
+
add();
|
|
1002
|
+
add("NEXT ACTION", { bold: true });
|
|
1003
|
+
add(presentation.next);
|
|
940
1004
|
}
|
|
941
1005
|
|
|
942
1006
|
add();
|
|
943
|
-
add(`
|
|
1007
|
+
add(`OUTPUT ${run.child ? "● live" : "○ idle"}`, {
|
|
944
1008
|
bold: true,
|
|
945
1009
|
color: run.child ? "green" : undefined,
|
|
946
1010
|
});
|
|
@@ -1036,7 +1100,7 @@ function isFullWidthCodePoint(codePoint) {
|
|
|
1036
1100
|
|
|
1037
1101
|
function terminalSize(stdout) {
|
|
1038
1102
|
return {
|
|
1039
|
-
columns: Math.max(
|
|
1103
|
+
columns: Math.max(52, stdout.columns || 120),
|
|
1040
1104
|
rows: Math.max(24, stdout.rows || 32),
|
|
1041
1105
|
};
|
|
1042
1106
|
}
|
|
@@ -1061,15 +1125,15 @@ function statusGlyph(run) {
|
|
|
1061
1125
|
}
|
|
1062
1126
|
|
|
1063
1127
|
function statusLabel(run) {
|
|
1064
|
-
if (run.controlState === "paused") return "
|
|
1065
|
-
if (run.controlState === "stopping") return "
|
|
1066
|
-
if (run.controlState === "stopped" || run.status === "stopped") return "
|
|
1067
|
-
if (run.status === "pr_ready_for_review") return "PR
|
|
1128
|
+
if (run.controlState === "paused") return "Paused";
|
|
1129
|
+
if (run.controlState === "stopping") return "Stopping";
|
|
1130
|
+
if (run.controlState === "stopped" || run.status === "stopped") return "Stopped";
|
|
1131
|
+
if (run.status === "pr_ready_for_review") return "PR ready · Review needed";
|
|
1068
1132
|
if (run.controlState === "waiting_human" || run.status === "waiting_human") {
|
|
1069
|
-
return "
|
|
1133
|
+
return "Waiting for you";
|
|
1070
1134
|
}
|
|
1071
|
-
if (run.status === "merge_ready") return "
|
|
1072
|
-
return String(run.status || "queued").replaceAll("_", " ")
|
|
1135
|
+
if (run.status === "merge_ready") return "Ready to merge";
|
|
1136
|
+
return titleCase(String(run.status || "queued").replaceAll("_", " "));
|
|
1073
1137
|
}
|
|
1074
1138
|
|
|
1075
1139
|
function statusColor(run) {
|
|
@@ -1078,3 +1142,61 @@ function statusColor(run) {
|
|
|
1078
1142
|
if (run.controlState === "complete") return "green";
|
|
1079
1143
|
return "cyan";
|
|
1080
1144
|
}
|
|
1145
|
+
|
|
1146
|
+
function nextActionForRun(run, repositoryMissing = false) {
|
|
1147
|
+
if (!run) return null;
|
|
1148
|
+
if (repositoryMissing) {
|
|
1149
|
+
return {
|
|
1150
|
+
tone: "yellow",
|
|
1151
|
+
text: "Press s to choose and authorize the local repository.",
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
if (run.controlState === "paused") {
|
|
1155
|
+
return { tone: "yellow", text: "Press p to resume the local agent." };
|
|
1156
|
+
}
|
|
1157
|
+
if (run.status === "pr_ready_for_review") {
|
|
1158
|
+
return { tone: "yellow", text: "Review the pull request, then press r to refresh its evidence." };
|
|
1159
|
+
}
|
|
1160
|
+
if (run.status === "merge_ready") {
|
|
1161
|
+
return {
|
|
1162
|
+
tone: "green",
|
|
1163
|
+
text: "Review the final evidence and merge the pull request when ready.",
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
if (run.controlState === "waiting_human" || run.status === "waiting_human") {
|
|
1167
|
+
return { tone: "yellow", text: "Press i, type your guidance or approval, then press Enter." };
|
|
1168
|
+
}
|
|
1169
|
+
const recoveryAction = primaryRecoveryAction(run);
|
|
1170
|
+
if (recoveryAction) {
|
|
1171
|
+
const command = recoveryAction.command ? ` Run: ${recoveryAction.command}` : "";
|
|
1172
|
+
return {
|
|
1173
|
+
tone: ["blocked", "failed"].includes(run.status) ? "red" : "yellow",
|
|
1174
|
+
text: `${recoveryAction.label || "Resolve the reported issue."}${command}`,
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
if (["blocked", "failed"].includes(run.status)) {
|
|
1178
|
+
return { tone: "red", text: "Press i to revise the request or provide recovery guidance." };
|
|
1179
|
+
}
|
|
1180
|
+
if (run.controlState === "stopped" || run.status === "stopped") {
|
|
1181
|
+
return { tone: undefined, text: "Press / and enter /new to start another task." };
|
|
1182
|
+
}
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
function primaryRecoveryAction(run) {
|
|
1187
|
+
const events = Array.isArray(run.snapshot?.events) ? [...run.snapshot.events].reverse() : [];
|
|
1188
|
+
for (const event of events) {
|
|
1189
|
+
const recovery = event?.detail?.recovery;
|
|
1190
|
+
if (!recovery || !Array.isArray(recovery.actions)) continue;
|
|
1191
|
+
return (
|
|
1192
|
+
recovery.actions.find((action) => action.id === recovery.primary_action_id) ??
|
|
1193
|
+
recovery.actions[0] ??
|
|
1194
|
+
null
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
return null;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
function titleCase(value) {
|
|
1201
|
+
return value.replace(/\b\p{L}/gu, (character) => character.toUpperCase());
|
|
1202
|
+
}
|
package/src/desktop-executor.js
CHANGED
|
@@ -47,6 +47,7 @@ export function desktopExecutorCapability(inspection) {
|
|
|
47
47
|
|
|
48
48
|
export async function resolveDesktopExecutorCommand({
|
|
49
49
|
backend,
|
|
50
|
+
model = null,
|
|
50
51
|
instruction,
|
|
51
52
|
repositoryPath,
|
|
52
53
|
executionKind = "agentic_loop",
|
|
@@ -61,6 +62,13 @@ export async function resolveDesktopExecutorCommand({
|
|
|
61
62
|
throw new Error("Desktop execution kind must be agentic_loop or direct.");
|
|
62
63
|
}
|
|
63
64
|
const normalizedPermissionMode = normalizePermissionMode(permissionMode);
|
|
65
|
+
const normalizedModel =
|
|
66
|
+
model === null || model === undefined
|
|
67
|
+
? null
|
|
68
|
+
: String(model).trim();
|
|
69
|
+
if (normalizedModel !== null && !/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/.test(normalizedModel)) {
|
|
70
|
+
throw new Error("Desktop executor model is invalid.");
|
|
71
|
+
}
|
|
64
72
|
const target = normalizeDesktopExecutionTarget(DESKTOP_EXECUTION_TARGET);
|
|
65
73
|
const override =
|
|
66
74
|
provider === "claude" ? environment.SIMY_CLAUDE_COMMAND : environment.SIMY_CODEX_COMMAND;
|
|
@@ -87,14 +95,20 @@ export async function resolveDesktopExecutorCommand({
|
|
|
87
95
|
args:
|
|
88
96
|
provider === "claude"
|
|
89
97
|
? directExecution
|
|
90
|
-
? claudeDirectBackendArgs(
|
|
98
|
+
? claudeDirectBackendArgs(
|
|
99
|
+
instruction,
|
|
100
|
+
normalizedPermissionMode,
|
|
101
|
+
normalizedModel,
|
|
102
|
+
)
|
|
91
103
|
: claudeBackendArgs(instruction)
|
|
92
104
|
: directExecution
|
|
93
105
|
? [
|
|
94
106
|
"exec",
|
|
95
107
|
"--json",
|
|
108
|
+
"--skip-git-repo-check",
|
|
96
109
|
"--sandbox",
|
|
97
110
|
normalizedPermissionMode === "read_only" ? "read-only" : "workspace-write",
|
|
111
|
+
...(normalizedModel ? ["--model", normalizedModel] : []),
|
|
98
112
|
instruction,
|
|
99
113
|
]
|
|
100
114
|
: ["exec", "--json", instruction],
|
|
@@ -123,7 +137,11 @@ export function claudeBackendArgs(instruction) {
|
|
|
123
137
|
];
|
|
124
138
|
}
|
|
125
139
|
|
|
126
|
-
export function claudeDirectBackendArgs(
|
|
140
|
+
export function claudeDirectBackendArgs(
|
|
141
|
+
instruction,
|
|
142
|
+
permissionMode = "read_only",
|
|
143
|
+
model = null,
|
|
144
|
+
) {
|
|
127
145
|
const normalized = normalizePermissionMode(permissionMode);
|
|
128
146
|
return [
|
|
129
147
|
"-p",
|
|
@@ -131,6 +149,7 @@ export function claudeDirectBackendArgs(instruction, permissionMode = "read_only
|
|
|
131
149
|
"--output-format",
|
|
132
150
|
"stream-json",
|
|
133
151
|
"--verbose",
|
|
152
|
+
...(model ? ["--model", model] : []),
|
|
134
153
|
...(normalized === "read_only"
|
|
135
154
|
? ["--permission-mode", "plan"]
|
|
136
155
|
: ["--dangerously-skip-permissions"]),
|