@scotthuang/agent-knock-knock 0.13.2 → 0.13.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.
- package/CHANGELOG.md +22 -0
- package/README.md +17 -4
- package/dist/src/claude-terminal-agent-adapter.js +4 -0
- package/dist/src/claude-terminal-agent-adapter.js.map +1 -1
- package/dist/src/cli-core.js +9 -1
- package/dist/src/cli-core.js.map +1 -1
- package/dist/src/codex-terminal-agent-adapter.js +4 -0
- package/dist/src/codex-terminal-agent-adapter.js.map +1 -1
- package/dist/src/herdr-terminal-control-provider.js +6 -1
- package/dist/src/herdr-terminal-control-provider.js.map +1 -1
- package/dist/src/host-adapter.d.ts +1 -1
- package/dist/src/host-adapter.js +1 -1
- package/dist/src/host-bridge-tools.js +2 -2
- package/dist/src/managed-session.js +2 -18
- package/dist/src/managed-session.js.map +1 -1
- package/dist/src/native-thread-lifecycle-cli-adapter.d.ts +13 -0
- package/dist/src/native-thread-lifecycle-cli-adapter.js +578 -2
- package/dist/src/native-thread-lifecycle-cli-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-command-adapter.js +325 -35
- package/dist/src/openclaw-plugin-command-adapter.js.map +1 -1
- package/dist/src/openclaw-plugin-helpers.d.ts +27 -0
- package/dist/src/openclaw-plugin-helpers.js +682 -2
- package/dist/src/openclaw-plugin-helpers.js.map +1 -1
- package/dist/src/openclaw-plugin-schemas.d.ts +53 -0
- package/dist/src/openclaw-plugin-schemas.js +53 -0
- package/dist/src/openclaw-plugin-schemas.js.map +1 -1
- package/dist/src/terminal-action-projection.d.ts +83 -1
- package/dist/src/terminal-action-projection.js +174 -0
- package/dist/src/terminal-action-projection.js.map +1 -1
- package/dist/src/terminal-agent-adapter.d.ts +7 -0
- package/dist/src/terminal-agent-adapter.js +8 -0
- package/dist/src/terminal-agent-adapter.js.map +1 -1
- package/dist/src/terminal-agent-bridge.d.ts +29 -0
- package/dist/src/terminal-agent-bridge.js +580 -14
- package/dist/src/terminal-agent-bridge.js.map +1 -1
- package/dist/src/terminal-control-ref.d.ts +23 -0
- package/dist/src/terminal-control-ref.js +32 -0
- package/dist/src/terminal-control-ref.js.map +1 -1
- package/dist/src/terminal-list-cli-adapter.js +324 -16
- package/dist/src/terminal-list-cli-adapter.js.map +1 -1
- package/dist/src/terminal-list-renderer.d.ts +1 -0
- package/dist/src/terminal-list-renderer.js +87 -3
- package/dist/src/terminal-list-renderer.js.map +1 -1
- package/dist/src/terminal-model-control.d.ts +312 -0
- package/dist/src/terminal-model-control.js +1595 -0
- package/dist/src/terminal-model-control.js.map +1 -0
- package/dist/src/terminal-runtime-cli-adapter.d.ts +2 -0
- package/dist/src/terminal-runtime-cli-adapter.js +59 -17
- package/dist/src/terminal-runtime-cli-adapter.js.map +1 -1
- package/docs/quickstart-herdr.md +1 -1
- package/docs/quickstart-tmux.md +27 -2
- package/openclaw.plugin.json +13 -1
- package/package.json +1 -1
- package/templates/openclaw-skills/agent-knock-knock/SKILL.md +54 -11
|
@@ -239,6 +239,43 @@ function parseAkkLifecycleCommand(action, rest) {
|
|
|
239
239
|
}
|
|
240
240
|
return { action: "list-resumable-threads", terminalId };
|
|
241
241
|
}
|
|
242
|
+
if (action === "models" || action === "model-options") {
|
|
243
|
+
const usage = "Usage: /akk models <exact-terminal-id>";
|
|
244
|
+
const { token: terminalId, rest: extra } = takeRequiredToken(rest, usage);
|
|
245
|
+
assertExactTerminalId(terminalId, usage);
|
|
246
|
+
if (extra.trim()) {
|
|
247
|
+
throw new Error(usage);
|
|
248
|
+
}
|
|
249
|
+
return { action: "model-options", terminalId };
|
|
250
|
+
}
|
|
251
|
+
if (action === "repair-model-control") {
|
|
252
|
+
const usage = "Usage: /akk repair-model-control <exact-terminal-id>";
|
|
253
|
+
const { token: terminalId, rest: extra } = takeRequiredToken(rest, usage);
|
|
254
|
+
assertExactTerminalId(terminalId, usage);
|
|
255
|
+
if (extra.trim()) {
|
|
256
|
+
throw new Error(usage);
|
|
257
|
+
}
|
|
258
|
+
return { action: "repair-model-control", terminalId };
|
|
259
|
+
}
|
|
260
|
+
if (action === "set-model") {
|
|
261
|
+
const usage = "Usage: /akk set-model <exact-terminal-id> <advertised-model-id> " +
|
|
262
|
+
"<advertised-reasoning-effort>";
|
|
263
|
+
const { token: terminalId, rest: modelInput } = takeRequiredToken(rest, usage);
|
|
264
|
+
assertExactTerminalId(terminalId, usage);
|
|
265
|
+
const { token: model, rest: effortInput } = takeRequiredToken(modelInput, usage);
|
|
266
|
+
const { token: reasoningEffort, rest: extra } = takeRequiredToken(effortInput, usage);
|
|
267
|
+
if (extra.trim() ||
|
|
268
|
+
!isAkkModelSemanticIdentifier(model, 160) ||
|
|
269
|
+
!/^[a-z][a-z0-9_-]{0,63}$/u.test(reasoningEffort)) {
|
|
270
|
+
throw new Error(usage);
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
action: "set-model",
|
|
274
|
+
terminalId,
|
|
275
|
+
model,
|
|
276
|
+
reasoningEffort
|
|
277
|
+
};
|
|
278
|
+
}
|
|
242
279
|
if (action === "new-thread" || action === "clear-thread") {
|
|
243
280
|
const usage = `Usage: /akk ${action} <exact-terminal-id>`;
|
|
244
281
|
const { token: terminalId, rest: extra } = takeRequiredToken(rest, usage);
|
|
@@ -378,6 +415,9 @@ export function akkUsageText() {
|
|
|
378
415
|
"/akk watch <exact-terminal-id>",
|
|
379
416
|
"/akk unwatch <watch-id>",
|
|
380
417
|
"/akk threads <exact-terminal-id>",
|
|
418
|
+
"/akk models <exact-terminal-id>",
|
|
419
|
+
"/akk repair-model-control <exact-terminal-id>",
|
|
420
|
+
"/akk set-model <exact-terminal-id> <advertised-model-id> <advertised-reasoning-effort>",
|
|
381
421
|
"/akk new-thread <exact-terminal-id>",
|
|
382
422
|
"/akk clear-thread <exact-terminal-id>",
|
|
383
423
|
"/akk resume-thread <exact-terminal-id> [uuid|previous|number|@short-id]",
|
|
@@ -385,7 +425,10 @@ export function akkUsageText() {
|
|
|
385
425
|
"/akk status [turn-selector|terminal-watch-id]",
|
|
386
426
|
"/akk respond <turn-selector>: <answer>",
|
|
387
427
|
"/akk approve <turn-selector> [approve_once|reject]",
|
|
388
|
-
"/akk cancel <turn-selector>"
|
|
428
|
+
"/akk cancel <turn-selector>",
|
|
429
|
+
"Model control targets one exact physical pane; Codex rollout attribution and identify-foreground are not prerequisites.",
|
|
430
|
+
"Repair-model-control is advertised only for an exact stale Codex /model Composer surface or open native picker; it clears no other draft, never presses Enter, and never submits or selects anything.",
|
|
431
|
+
"Models normally requires an idle, empty, prompt-free UI with no active Turn; for an exactly advertised Codex 0.154 /model residue it may continue that residue into read-only catalog discovery. Successful discovery consumes any residual-entry authority, restores the exact empty UI, and issues a fresh ordinary one-shot set-model offer; set-model accepts only that offer. If the result is uncertain, inspect the pane and do not retry automatically."
|
|
389
432
|
].join("\n");
|
|
390
433
|
}
|
|
391
434
|
export function formatAkkListCommandResult(result) {
|
|
@@ -485,6 +528,500 @@ export function formatAkkListCommandResult(result) {
|
|
|
485
528
|
: [])
|
|
486
529
|
].join("\n");
|
|
487
530
|
}
|
|
531
|
+
/**
|
|
532
|
+
* Keep every structured controller-Host List result below its model-output
|
|
533
|
+
* budget without weakening the private action-authority path.
|
|
534
|
+
*
|
|
535
|
+
* The CLI remains the complete operator/debug contract. The controller receives a
|
|
536
|
+
* terminal-first projection: current resource identities and state, action
|
|
537
|
+
* names, and only the dynamic semantic inputs needed to invoke those actions.
|
|
538
|
+
* Static explanations live in the bundled agent-knock-knock skill.
|
|
539
|
+
*/
|
|
540
|
+
export function compactAkkListModelProjection(value) {
|
|
541
|
+
const result = recordValue(value) ?? {};
|
|
542
|
+
const actionContracts = recordValue(result.action_contracts);
|
|
543
|
+
const diagnostics = compactAkkListDiagnostics(result);
|
|
544
|
+
const recovery = compactAkkListControlState(result.recovery);
|
|
545
|
+
return {
|
|
546
|
+
projection: {
|
|
547
|
+
schema: "agent-knock-knock/host-list-compact",
|
|
548
|
+
version: 1,
|
|
549
|
+
skill: "agent-knock-knock",
|
|
550
|
+
...(finiteNumber(actionContracts?.version) !== undefined
|
|
551
|
+
? { action_contract_version: finiteNumber(actionContracts?.version) }
|
|
552
|
+
: {})
|
|
553
|
+
},
|
|
554
|
+
terminals: arrayValue(result.terminals).map(compactAkkListTerminal),
|
|
555
|
+
terminal_watches: arrayValue(result.terminal_watches)
|
|
556
|
+
.map(compactAkkListWatch),
|
|
557
|
+
unavailable_managed_turns: arrayValue(result.unavailable_managed_turns)
|
|
558
|
+
.map((turn) => compactAkkListTurn(turn, true)),
|
|
559
|
+
...(recovery ? { recovery } : {}),
|
|
560
|
+
...(Object.keys(diagnostics).length > 0 ? { diagnostics } : {})
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function compactAkkListTerminal(terminal) {
|
|
564
|
+
const output = compactAkkListScalars(terminal, [
|
|
565
|
+
"id",
|
|
566
|
+
"short_ref",
|
|
567
|
+
"source",
|
|
568
|
+
"agent",
|
|
569
|
+
"agent_version",
|
|
570
|
+
"pid",
|
|
571
|
+
"cwd",
|
|
572
|
+
"process_state",
|
|
573
|
+
"screen_state",
|
|
574
|
+
"activity_state",
|
|
575
|
+
"durable_activity_state",
|
|
576
|
+
"native_identity_state",
|
|
577
|
+
"native_agent_session_id",
|
|
578
|
+
"management_state",
|
|
579
|
+
"handoff_state",
|
|
580
|
+
"confidence"
|
|
581
|
+
]);
|
|
582
|
+
const workspace = nonEmptyString(terminal.workspace);
|
|
583
|
+
if (workspace && workspace !== nonEmptyString(terminal.cwd)) {
|
|
584
|
+
output.workspace = workspace;
|
|
585
|
+
}
|
|
586
|
+
const command = nonEmptyString(terminal.command);
|
|
587
|
+
if (command)
|
|
588
|
+
output.command = truncateText(command, 160);
|
|
589
|
+
compactAkkListNested(output, "terminal_control", terminal.terminal_control, ["kind", "target"]);
|
|
590
|
+
compactAkkListNested(output, "approval_state", terminal.approval_state, [
|
|
591
|
+
"scanned", "blocked", "approvable", "state", "kind", "decision_mode"
|
|
592
|
+
]);
|
|
593
|
+
const features = compactAkkListFeatures(terminal);
|
|
594
|
+
if (Object.keys(features).length > 0)
|
|
595
|
+
output.features = features;
|
|
596
|
+
const managed = compactAkkListManaged(terminal.managed);
|
|
597
|
+
if (managed)
|
|
598
|
+
output.managed = managed;
|
|
599
|
+
const managementConflict = compactAkkListControlState(terminal.management_conflict);
|
|
600
|
+
if (managementConflict)
|
|
601
|
+
output.management_conflict = managementConflict;
|
|
602
|
+
const managementUnavailable = recordValue(terminal.management_unavailable);
|
|
603
|
+
if (managementUnavailable)
|
|
604
|
+
output.management_unavailable = true;
|
|
605
|
+
const handoffDecision = compactAkkListHandoffDecision(terminal.handoff_decision);
|
|
606
|
+
if (handoffDecision)
|
|
607
|
+
output.handoff_decision = handoffDecision;
|
|
608
|
+
const blockingTurns = arrayValue(terminal.blocking_turns)
|
|
609
|
+
.map(compactAkkListBlockingTurn);
|
|
610
|
+
if (blockingTurns.length > 0)
|
|
611
|
+
output.blocking_turns = blockingTurns;
|
|
612
|
+
const managedRecord = recordValue(terminal.managed);
|
|
613
|
+
const currentTurn = recordValue(managedRecord?.current_turn);
|
|
614
|
+
Object.assign(output, compactAkkListActions(terminal.available_actions, {
|
|
615
|
+
terminal_id: terminal.id,
|
|
616
|
+
conversation_id: terminal.id,
|
|
617
|
+
session_id: managedRecord?.session_id,
|
|
618
|
+
turn_id: currentTurn?.turn_id ?? currentTurn?.conversation_id ??
|
|
619
|
+
currentTurn?.id
|
|
620
|
+
}));
|
|
621
|
+
addAkkListWarningFlags(output, terminal);
|
|
622
|
+
return output;
|
|
623
|
+
}
|
|
624
|
+
function compactAkkListFeatures(terminal) {
|
|
625
|
+
const output = {};
|
|
626
|
+
const lifecycle = recordValue(terminal.native_thread_lifecycle);
|
|
627
|
+
const inspection = recordValue(terminal.native_inspection);
|
|
628
|
+
const modelControl = recordValue(terminal.model_control);
|
|
629
|
+
if (typeof lifecycle?.status === "string") {
|
|
630
|
+
output.native_threads = lifecycle.status;
|
|
631
|
+
}
|
|
632
|
+
if (typeof inspection?.status === "string") {
|
|
633
|
+
output.native_inspection = inspection.status;
|
|
634
|
+
}
|
|
635
|
+
if (typeof modelControl?.status === "string") {
|
|
636
|
+
const model = compactAkkListScalars(modelControl, ["status", "scope"]);
|
|
637
|
+
output.model_control = Object.keys(model).length === 1
|
|
638
|
+
? model.status
|
|
639
|
+
: model;
|
|
640
|
+
}
|
|
641
|
+
return output;
|
|
642
|
+
}
|
|
643
|
+
function compactAkkListManaged(value) {
|
|
644
|
+
const managed = recordValue(value);
|
|
645
|
+
if (!managed)
|
|
646
|
+
return undefined;
|
|
647
|
+
const output = compactAkkListScalars(managed, [
|
|
648
|
+
"session_id",
|
|
649
|
+
"session_short_ref",
|
|
650
|
+
"native_thread_id",
|
|
651
|
+
"binding_status",
|
|
652
|
+
"turn_count",
|
|
653
|
+
"hidden_turn_count",
|
|
654
|
+
"session_count"
|
|
655
|
+
]);
|
|
656
|
+
const currentTurn = recordValue(managed.current_turn);
|
|
657
|
+
const recentTurn = recordValue(managed.recent_turn);
|
|
658
|
+
output.current_turn = currentTurn ? compactAkkListTurn(currentTurn) : null;
|
|
659
|
+
output.recent_turn = recentTurn ? compactAkkListTurn(recentTurn) : null;
|
|
660
|
+
const history = arrayValue(managed.history)
|
|
661
|
+
.map((turn) => compactAkkListTurn(turn));
|
|
662
|
+
if (history.length > 0)
|
|
663
|
+
output.history = history;
|
|
664
|
+
return output;
|
|
665
|
+
}
|
|
666
|
+
function compactAkkListTurn(turn, standalone = false) {
|
|
667
|
+
const output = compactAkkListScalars(turn, [
|
|
668
|
+
"short_ref",
|
|
669
|
+
"status",
|
|
670
|
+
"lifecycle_state",
|
|
671
|
+
"session_id",
|
|
672
|
+
"turn_id",
|
|
673
|
+
"updated_at",
|
|
674
|
+
"response_rounds_used"
|
|
675
|
+
]);
|
|
676
|
+
const turnId = nonEmptyString(turn.turn_id ?? turn.conversation_id ?? turn.id);
|
|
677
|
+
if (turnId)
|
|
678
|
+
output.turn_id = turnId;
|
|
679
|
+
if (standalone) {
|
|
680
|
+
Object.assign(output, compactAkkListScalars(turn, [
|
|
681
|
+
"source", "agent", "workspace", "created_at"
|
|
682
|
+
]));
|
|
683
|
+
}
|
|
684
|
+
compactAkkListNested(output, "approval_state", turn.approval_state, [
|
|
685
|
+
"scanned", "blocked", "approvable", "state", "kind", "decision_mode"
|
|
686
|
+
]);
|
|
687
|
+
const interaction = compactAkkListInteraction(turn.interaction_state);
|
|
688
|
+
if (interaction)
|
|
689
|
+
output.interaction_state = interaction;
|
|
690
|
+
const terminalAvailability = compactAkkListControlState(turn.terminal_availability);
|
|
691
|
+
if (terminalAvailability) {
|
|
692
|
+
output.terminal_availability = terminalAvailability;
|
|
693
|
+
}
|
|
694
|
+
else if (typeof turn.terminal_availability === "string" ||
|
|
695
|
+
typeof turn.terminal_availability === "boolean") {
|
|
696
|
+
output.terminal_availability = turn.terminal_availability;
|
|
697
|
+
}
|
|
698
|
+
Object.assign(output, compactAkkListActions(turn.available_actions, {
|
|
699
|
+
session_id: turn.session_id,
|
|
700
|
+
turn_id: turnId,
|
|
701
|
+
conversation_id: turnId
|
|
702
|
+
}));
|
|
703
|
+
addAkkListWarningFlags(output, turn);
|
|
704
|
+
return output;
|
|
705
|
+
}
|
|
706
|
+
function compactAkkListWatch(watch) {
|
|
707
|
+
const output = compactAkkListScalars(watch, [
|
|
708
|
+
"watch_id",
|
|
709
|
+
"short_ref",
|
|
710
|
+
"source",
|
|
711
|
+
"watch_mode",
|
|
712
|
+
"confidence",
|
|
713
|
+
"interaction_policy",
|
|
714
|
+
"agent",
|
|
715
|
+
"terminal_id",
|
|
716
|
+
"native_thread_id",
|
|
717
|
+
"workspace",
|
|
718
|
+
"status",
|
|
719
|
+
"activity_state",
|
|
720
|
+
"created_at",
|
|
721
|
+
"deadline_at",
|
|
722
|
+
"updated_at",
|
|
723
|
+
"last_activity_at"
|
|
724
|
+
]);
|
|
725
|
+
compactAkkListNested(output, "capabilities", watch.capabilities, [
|
|
726
|
+
"interaction_notify", "interaction_respond"
|
|
727
|
+
]);
|
|
728
|
+
compactAkkListNested(output, "callback", watch.callback, [
|
|
729
|
+
"pending", "delivered", "failed", "superseded", "last_error_code"
|
|
730
|
+
]);
|
|
731
|
+
const interaction = compactAkkListInteraction(watch.interaction_state);
|
|
732
|
+
if (interaction)
|
|
733
|
+
output.interaction_state = interaction;
|
|
734
|
+
const settlement = recordValue(watch.settlement);
|
|
735
|
+
if (settlement) {
|
|
736
|
+
const compactSettlement = compactAkkListScalars(settlement, [
|
|
737
|
+
"kind", "observed_at", "reason_code", "completion_id",
|
|
738
|
+
"completion_timestamp"
|
|
739
|
+
]);
|
|
740
|
+
if (Object.keys(compactSettlement).length > 0) {
|
|
741
|
+
output.settlement = compactSettlement;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
Object.assign(output, compactAkkListActions(watch.available_actions, {
|
|
745
|
+
terminal_id: watch.terminal_id,
|
|
746
|
+
watch_id: watch.watch_id
|
|
747
|
+
}));
|
|
748
|
+
addAkkListWarningFlags(output, watch);
|
|
749
|
+
return output;
|
|
750
|
+
}
|
|
751
|
+
function compactAkkListInteraction(value) {
|
|
752
|
+
const interaction = recordValue(value);
|
|
753
|
+
if (!interaction)
|
|
754
|
+
return undefined;
|
|
755
|
+
const output = compactAkkListScalars(interaction, [
|
|
756
|
+
"state", "response_kind", "current_step", "total_steps",
|
|
757
|
+
"response_authority"
|
|
758
|
+
]);
|
|
759
|
+
compactAkkListNested(output, "capabilities", interaction.capabilities, [
|
|
760
|
+
"respond", "single_select", "multi_select", "free_text", "confirm"
|
|
761
|
+
]);
|
|
762
|
+
return Object.keys(output).length > 0 ? output : undefined;
|
|
763
|
+
}
|
|
764
|
+
function compactAkkListActions(value, parentIds = {}) {
|
|
765
|
+
const actions = recordValue(value);
|
|
766
|
+
if (!actions)
|
|
767
|
+
return {};
|
|
768
|
+
const availableActions = {};
|
|
769
|
+
const actionInputs = {};
|
|
770
|
+
for (const [name, actionValue] of Object.entries(actions)) {
|
|
771
|
+
const action = recordValue(actionValue);
|
|
772
|
+
if (!action)
|
|
773
|
+
continue;
|
|
774
|
+
availableActions[name] = true;
|
|
775
|
+
const input = compactAkkListActionInput(name, action, parentIds);
|
|
776
|
+
if (Object.keys(input).length > 0)
|
|
777
|
+
actionInputs[name] = input;
|
|
778
|
+
}
|
|
779
|
+
return {
|
|
780
|
+
...(Object.keys(availableActions).length > 0
|
|
781
|
+
? { available_actions: availableActions }
|
|
782
|
+
: {}),
|
|
783
|
+
...(Object.keys(actionInputs).length > 0
|
|
784
|
+
? { action_inputs: actionInputs }
|
|
785
|
+
: {})
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function compactAkkListActionInput(name, action, parentIds = {}) {
|
|
789
|
+
const output = {};
|
|
790
|
+
const tool = nonEmptyString(action.tool);
|
|
791
|
+
const expectedTool = name === "retry_submission"
|
|
792
|
+
? "agent_knock_knock_send"
|
|
793
|
+
: `agent_knock_knock_${name}`;
|
|
794
|
+
if (tool && tool !== expectedTool)
|
|
795
|
+
output.tool = tool;
|
|
796
|
+
const args = compactAkkListActionArguments(action.arguments, name, parentIds);
|
|
797
|
+
if (Object.keys(args).length > 0)
|
|
798
|
+
output.arguments = args;
|
|
799
|
+
const missingRequired = stringArrayValue(action.missing_required)
|
|
800
|
+
.filter((field) => !isAkkModelFacingPrivateAuthorityField(field));
|
|
801
|
+
if (missingRequired.length > 0) {
|
|
802
|
+
output.missing_required = missingRequired;
|
|
803
|
+
}
|
|
804
|
+
for (const key of ["scope", "decision_mode"]) {
|
|
805
|
+
const item = action[key];
|
|
806
|
+
if (typeof item === "string" ||
|
|
807
|
+
typeof item === "boolean" ||
|
|
808
|
+
typeof item === "number")
|
|
809
|
+
output[key] = item;
|
|
810
|
+
}
|
|
811
|
+
for (const key of ["decisions", "choices"]) {
|
|
812
|
+
const values = stringArrayValue(action[key]);
|
|
813
|
+
if (values.length > 0)
|
|
814
|
+
output[key] = values;
|
|
815
|
+
}
|
|
816
|
+
return output;
|
|
817
|
+
}
|
|
818
|
+
function compactAkkListActionArguments(value, actionName, parentIds) {
|
|
819
|
+
const args = recordValue(value);
|
|
820
|
+
if (!args)
|
|
821
|
+
return {};
|
|
822
|
+
const output = {};
|
|
823
|
+
for (const [key, item] of Object.entries(args)) {
|
|
824
|
+
if (isAkkModelFacingPrivateAuthorityField(key) ||
|
|
825
|
+
["request", "text", "body", "description", "instruction", "use"]
|
|
826
|
+
.includes(key))
|
|
827
|
+
continue;
|
|
828
|
+
const compact = compactAkkListSemanticValue(item, 0);
|
|
829
|
+
if (compact !== undefined)
|
|
830
|
+
output[key] = compact;
|
|
831
|
+
}
|
|
832
|
+
const selector = nonEmptyString(output.selector);
|
|
833
|
+
if (actionName === "send" &&
|
|
834
|
+
selector &&
|
|
835
|
+
/^terminal:v[0-9]+:\S+$/u.test(selector)) {
|
|
836
|
+
delete output.selector;
|
|
837
|
+
output.terminal_id = selector;
|
|
838
|
+
}
|
|
839
|
+
const conversationId = nonEmptyString(output.conversation_id);
|
|
840
|
+
if (actionName === "approve" &&
|
|
841
|
+
conversationId &&
|
|
842
|
+
/^terminal:v[0-9]+:\S+$/u.test(conversationId)) {
|
|
843
|
+
delete output.conversation_id;
|
|
844
|
+
output.terminal_id = conversationId;
|
|
845
|
+
}
|
|
846
|
+
for (const key of [
|
|
847
|
+
"terminal_id", "session_id", "turn_id", "conversation_id", "watch_id"
|
|
848
|
+
]) {
|
|
849
|
+
if (output[key] !== undefined && output[key] === parentIds[key]) {
|
|
850
|
+
delete output[key];
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
return output;
|
|
854
|
+
}
|
|
855
|
+
function compactAkkListSemanticValue(value, depth) {
|
|
856
|
+
if (value === null ||
|
|
857
|
+
typeof value === "string" ||
|
|
858
|
+
typeof value === "boolean" ||
|
|
859
|
+
(typeof value === "number" && Number.isFinite(value)))
|
|
860
|
+
return value;
|
|
861
|
+
if (depth >= 3)
|
|
862
|
+
return undefined;
|
|
863
|
+
if (Array.isArray(value)) {
|
|
864
|
+
return value.slice(0, 32).flatMap((item) => {
|
|
865
|
+
const compact = compactAkkListSemanticValue(item, depth + 1);
|
|
866
|
+
return compact === undefined ? [] : [compact];
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
const record = recordValue(value);
|
|
870
|
+
if (!record)
|
|
871
|
+
return undefined;
|
|
872
|
+
const output = {};
|
|
873
|
+
for (const [key, item] of Object.entries(record)) {
|
|
874
|
+
if (isAkkModelFacingPrivateAuthorityField(key))
|
|
875
|
+
continue;
|
|
876
|
+
const compact = compactAkkListSemanticValue(item, depth + 1);
|
|
877
|
+
if (compact !== undefined)
|
|
878
|
+
output[key] = compact;
|
|
879
|
+
}
|
|
880
|
+
return output;
|
|
881
|
+
}
|
|
882
|
+
function compactAkkListHandoffDecision(value) {
|
|
883
|
+
const decision = recordValue(value);
|
|
884
|
+
if (!decision)
|
|
885
|
+
return undefined;
|
|
886
|
+
const output = compactAkkListScalars(decision, [
|
|
887
|
+
"kind", "source_session_id", "source_turn_id"
|
|
888
|
+
]);
|
|
889
|
+
const choices = recordValue(decision.choices);
|
|
890
|
+
if (choices) {
|
|
891
|
+
const compactChoices = {};
|
|
892
|
+
for (const [name, choiceValue] of Object.entries(choices)) {
|
|
893
|
+
const choice = recordValue(choiceValue);
|
|
894
|
+
if (!choice)
|
|
895
|
+
continue;
|
|
896
|
+
const action = compactAkkListNamedAction(choice.action);
|
|
897
|
+
compactChoices[name] = action ? { action } : true;
|
|
898
|
+
}
|
|
899
|
+
if (Object.keys(compactChoices).length > 0)
|
|
900
|
+
output.choices = compactChoices;
|
|
901
|
+
}
|
|
902
|
+
return Object.keys(output).length > 0 ? output : undefined;
|
|
903
|
+
}
|
|
904
|
+
function compactAkkListBlockingTurn(value) {
|
|
905
|
+
const output = compactAkkListScalars(value, [
|
|
906
|
+
"session_id", "turn_id", "status"
|
|
907
|
+
]);
|
|
908
|
+
const recoveryAction = compactAkkListNamedAction(value.recovery_action);
|
|
909
|
+
if (recoveryAction)
|
|
910
|
+
output.recovery_action = recoveryAction;
|
|
911
|
+
return output;
|
|
912
|
+
}
|
|
913
|
+
function compactAkkListNamedAction(value) {
|
|
914
|
+
const action = recordValue(value);
|
|
915
|
+
if (!action)
|
|
916
|
+
return undefined;
|
|
917
|
+
const tool = nonEmptyString(action.tool);
|
|
918
|
+
const name = tool?.replace(/^agent_knock_knock_/u, "") ?? "action";
|
|
919
|
+
const input = compactAkkListActionInput(name, action);
|
|
920
|
+
return {
|
|
921
|
+
name,
|
|
922
|
+
...(Object.keys(input).length > 0 ? input : {})
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
function compactAkkListControlState(value) {
|
|
926
|
+
const record = recordValue(value);
|
|
927
|
+
if (!record)
|
|
928
|
+
return undefined;
|
|
929
|
+
const output = {};
|
|
930
|
+
for (const [key, item] of Object.entries(record)) {
|
|
931
|
+
if (isAkkModelFacingPrivateAuthorityField(key) ||
|
|
932
|
+
/(?:reason|recovery|instruction|description|evidence|path)$/iu.test(key))
|
|
933
|
+
continue;
|
|
934
|
+
const keep = /(?:^|_)(?:id|state|status|kind|mode|scope|code|count|owner)$/u
|
|
935
|
+
.test(key) || typeof item === "boolean" || typeof item === "number";
|
|
936
|
+
if (!keep)
|
|
937
|
+
continue;
|
|
938
|
+
const compact = compactAkkListSemanticValue(item, 0);
|
|
939
|
+
if (compact !== undefined)
|
|
940
|
+
output[key] = compact;
|
|
941
|
+
}
|
|
942
|
+
return Object.keys(output).length > 0 ? output : undefined;
|
|
943
|
+
}
|
|
944
|
+
function compactAkkListDiagnostics(result) {
|
|
945
|
+
const output = {};
|
|
946
|
+
compactAkkListNested(output, "store", result.store, [
|
|
947
|
+
"status", "readable", "writable", "upgradeable", "format_version",
|
|
948
|
+
"writer_protocol"
|
|
949
|
+
]);
|
|
950
|
+
const reconciliation = recordValue(result.reconciliation);
|
|
951
|
+
if (reconciliation) {
|
|
952
|
+
const compact = compactAkkListDiagnosticRecord(reconciliation);
|
|
953
|
+
if (Object.keys(compact).length > 0)
|
|
954
|
+
output.reconciliation = compact;
|
|
955
|
+
}
|
|
956
|
+
const scan = recordValue(result.terminal_scan);
|
|
957
|
+
if (scan) {
|
|
958
|
+
const compact = compactAkkListDiagnosticRecord(scan);
|
|
959
|
+
if (Array.isArray(scan.agents)) {
|
|
960
|
+
compact.agents = scan.agents.filter((item) => typeof item === "string");
|
|
961
|
+
}
|
|
962
|
+
if (scan.diagnostics !== undefined)
|
|
963
|
+
compact.provider_diagnostics = true;
|
|
964
|
+
if (Object.keys(compact).length > 0)
|
|
965
|
+
output.terminal_scan = compact;
|
|
966
|
+
}
|
|
967
|
+
return output;
|
|
968
|
+
}
|
|
969
|
+
function compactAkkListDiagnosticRecord(value) {
|
|
970
|
+
const output = {};
|
|
971
|
+
for (const [key, item] of Object.entries(value)) {
|
|
972
|
+
if (key === "reason" ||
|
|
973
|
+
key === "diagnostics" ||
|
|
974
|
+
/(?:^|_)(?:path|dir)$/u.test(key))
|
|
975
|
+
continue;
|
|
976
|
+
if (typeof item === "boolean" ||
|
|
977
|
+
(typeof item === "number" && Number.isFinite(item)) ||
|
|
978
|
+
(typeof item === "string" && key !== "error"))
|
|
979
|
+
output[key] = item;
|
|
980
|
+
if (key === "error" && typeof item === "string") {
|
|
981
|
+
output.error = truncateText(item, 300);
|
|
982
|
+
}
|
|
983
|
+
if (Array.isArray(item) && key === "errors") {
|
|
984
|
+
output.error_count = item.length;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (nonEmptyString(value.reason) &&
|
|
988
|
+
!["ok", "healthy", "compatible", "disabled"]
|
|
989
|
+
.includes(nonEmptyString(value.status) ?? "")) {
|
|
990
|
+
output.message = truncateText(value.reason, 300);
|
|
991
|
+
}
|
|
992
|
+
return output;
|
|
993
|
+
}
|
|
994
|
+
function compactAkkListScalars(value, keys) {
|
|
995
|
+
const output = {};
|
|
996
|
+
for (const key of keys) {
|
|
997
|
+
const item = value[key];
|
|
998
|
+
if (item === null ||
|
|
999
|
+
typeof item === "string" ||
|
|
1000
|
+
typeof item === "boolean" ||
|
|
1001
|
+
(typeof item === "number" && Number.isFinite(item)))
|
|
1002
|
+
output[key] = item;
|
|
1003
|
+
}
|
|
1004
|
+
return output;
|
|
1005
|
+
}
|
|
1006
|
+
function compactAkkListNested(output, key, value, keys) {
|
|
1007
|
+
const record = recordValue(value);
|
|
1008
|
+
if (!record)
|
|
1009
|
+
return;
|
|
1010
|
+
const compact = compactAkkListScalars(record, keys);
|
|
1011
|
+
if (Object.keys(compact).length > 0)
|
|
1012
|
+
output[key] = compact;
|
|
1013
|
+
}
|
|
1014
|
+
function addAkkListWarningFlags(output, value) {
|
|
1015
|
+
const compatibilityWarnings = [
|
|
1016
|
+
nonEmptyString(value.compatibility_warning),
|
|
1017
|
+
...stringArrayValue(value.compatibility_warnings)
|
|
1018
|
+
].filter((item) => Boolean(item));
|
|
1019
|
+
if (compatibilityWarnings.length > 0)
|
|
1020
|
+
output.compatibility_warning = true;
|
|
1021
|
+
const warnings = stringArrayValue(value.warnings);
|
|
1022
|
+
if (warnings.length > 0)
|
|
1023
|
+
output.warning_count = warnings.length;
|
|
1024
|
+
}
|
|
488
1025
|
export function formatAkkWatchCommandResult(result) {
|
|
489
1026
|
const watch = terminalWatchRecord(result);
|
|
490
1027
|
return [
|
|
@@ -720,6 +1257,108 @@ export function formatAkkThreadsCommandResult(result) {
|
|
|
720
1257
|
"Listing or switching native threads does not create an AKK Turn."
|
|
721
1258
|
].join("\n");
|
|
722
1259
|
}
|
|
1260
|
+
export function formatAkkModelOptionsCommandResult(result) {
|
|
1261
|
+
const terminalId = nonEmptyString(result.terminal_id) ?? "unknown";
|
|
1262
|
+
const scope = nonEmptyString(result.scope) ?? "unknown";
|
|
1263
|
+
const current = recordValue(result.current) ?? {};
|
|
1264
|
+
const models = arrayValue(result.models);
|
|
1265
|
+
const modelLines = models.slice(0, 50).map((entry) => {
|
|
1266
|
+
const id = nonEmptyString(entry.id) ?? "unknown";
|
|
1267
|
+
const label = nonEmptyString(entry.label);
|
|
1268
|
+
const efforts = stringArrayValue(entry.reasoning_efforts);
|
|
1269
|
+
return `- ${id}${label && label !== id ? ` (${label})` : ""}` +
|
|
1270
|
+
`${efforts.length > 0 ? ` | reasoning: ${efforts.join(", ")}` : ""}`;
|
|
1271
|
+
});
|
|
1272
|
+
return [
|
|
1273
|
+
`AKK model options (${models.length} models):`,
|
|
1274
|
+
`terminal: ${terminalId}`,
|
|
1275
|
+
`agent: ${nonEmptyString(result.agent) ?? "unknown"}`,
|
|
1276
|
+
`scope: ${scope}`,
|
|
1277
|
+
`current model: ${nonEmptyString(current.model) ?? "unknown"}`,
|
|
1278
|
+
`current reasoning: ${nonEmptyString(current.reasoning_effort) ?? "unobserved"}`,
|
|
1279
|
+
...compatibilityWarningLines(result),
|
|
1280
|
+
...(modelLines.length > 0 ? ["models:", ...modelLines] : ["models: none"]),
|
|
1281
|
+
scope === "current_and_new_sessions"
|
|
1282
|
+
? "Codex applies this selection to the current session and persists the selected model and ordinary efforts (including max) for new sessions; ultra applies only now and uses a native non-ultra future fallback."
|
|
1283
|
+
: "Claude Code applies this selection only to the current session; new-session defaults are unchanged.",
|
|
1284
|
+
`next: /akk set-model ${terminalId} <advertised-model-id> <advertised-reasoning-effort>`,
|
|
1285
|
+
"This successful catalog result has restored an exact empty Composer and carries a fresh ordinary private offer for one set-model attempt in the current controller. Residual-entry authority was consumed by model-options and is never reusable by set-model. A Codex rollout and identify-foreground are not prerequisites. Refresh /akk models after any terminal change or failed attempt."
|
|
1286
|
+
].join("\n");
|
|
1287
|
+
}
|
|
1288
|
+
export function formatAkkSetModelCommandResult(result) {
|
|
1289
|
+
const outcome = nonEmptyString(result.outcome) ?? "uncertain";
|
|
1290
|
+
const requested = recordValue(result.requested) ?? {};
|
|
1291
|
+
const effective = recordValue(result.effective) ?? {};
|
|
1292
|
+
const newSessionDefaults = recordValue(result.new_session_defaults) ?? {};
|
|
1293
|
+
const uncertain = outcome === "uncertain" || result.do_not_retry === true;
|
|
1294
|
+
const defaultsChanged = typeof result.defaults_changed === "boolean"
|
|
1295
|
+
? result.defaults_changed ? "yes" : "no"
|
|
1296
|
+
: "unknown";
|
|
1297
|
+
const requestedUltra = requested.reasoning_effort === "ultra";
|
|
1298
|
+
return [
|
|
1299
|
+
outcome === "changed"
|
|
1300
|
+
? "AKK changed and verified the coding-agent model."
|
|
1301
|
+
: outcome === "already_effective"
|
|
1302
|
+
? "AKK verified that the requested model was already effective."
|
|
1303
|
+
: "AKK could not prove the complete model-change postcondition.",
|
|
1304
|
+
`terminal: ${nonEmptyString(result.terminal_id) ?? "unknown"}`,
|
|
1305
|
+
`scope: ${nonEmptyString(result.scope) ?? "unknown"}`,
|
|
1306
|
+
`requested model: ${nonEmptyString(requested.model) ?? "unknown"}`,
|
|
1307
|
+
`requested reasoning: ${nonEmptyString(requested.reasoning_effort) ?? "unchanged/default"}`,
|
|
1308
|
+
`effective model: ${nonEmptyString(effective.model) ?? "unproven"}`,
|
|
1309
|
+
`effective reasoning: ${nonEmptyString(effective.reasoning_effort) ?? "unproven"}`,
|
|
1310
|
+
`new-session default changed: ${defaultsChanged}`,
|
|
1311
|
+
...(nonEmptyString(newSessionDefaults.model)
|
|
1312
|
+
? [`new-session default model: ${nonEmptyString(newSessionDefaults.model)}`]
|
|
1313
|
+
: []),
|
|
1314
|
+
...(nonEmptyString(newSessionDefaults.reasoning_effort)
|
|
1315
|
+
? [`new-session default reasoning: ${nonEmptyString(newSessionDefaults.reasoning_effort)}`]
|
|
1316
|
+
: requestedUltra && result.scope === "current_and_new_sessions" && outcome === "changed"
|
|
1317
|
+
? ["new-session default reasoning: native non-ultra fallback (exact value is not exposed by this Codex TUI)"]
|
|
1318
|
+
: []),
|
|
1319
|
+
...compatibilityWarningLines(result),
|
|
1320
|
+
...(nonEmptyString(result.reason)
|
|
1321
|
+
? [
|
|
1322
|
+
`reason: ${sanitizeAkkModelFacingDiagnosticText(nonEmptyString(result.reason) ?? "")}`
|
|
1323
|
+
]
|
|
1324
|
+
: []),
|
|
1325
|
+
uncertain
|
|
1326
|
+
? "Next: do not retry automatically; inspect the shared pane, then refresh /akk models before any deliberate new attempt."
|
|
1327
|
+
: "No task, approval, questionnaire response, or AKK Turn was created."
|
|
1328
|
+
].join("\n");
|
|
1329
|
+
}
|
|
1330
|
+
export function formatAkkRepairModelControlCommandResult(result) {
|
|
1331
|
+
const outcome = nonEmptyString(result.outcome) ?? "uncertain";
|
|
1332
|
+
const repaired = outcome === "repaired";
|
|
1333
|
+
const uncertain = outcome === "uncertain" || result.do_not_retry === true;
|
|
1334
|
+
return [
|
|
1335
|
+
repaired
|
|
1336
|
+
? "AKK cleared and verified the exact stale Codex /model surface."
|
|
1337
|
+
: "AKK could not prove the model-control repair postcondition.",
|
|
1338
|
+
`terminal: ${nonEmptyString(result.terminal_id) ?? "unknown"}`,
|
|
1339
|
+
`composer postcondition: ${nonEmptyString(result.composer_postcondition) ?? "unproven"}`,
|
|
1340
|
+
`terminal input attempted: ${result.terminal_input_attempted === true ? "yes" : "no"}`,
|
|
1341
|
+
...compatibilityWarningLines(result),
|
|
1342
|
+
...(nonEmptyString(result.reason)
|
|
1343
|
+
? [
|
|
1344
|
+
`reason: ${sanitizeAkkModelFacingDiagnosticText(nonEmptyString(result.reason) ?? "")}`
|
|
1345
|
+
]
|
|
1346
|
+
: []),
|
|
1347
|
+
uncertain
|
|
1348
|
+
? "Next: do not retry automatically; inspect the shared pane and refresh /akk list."
|
|
1349
|
+
: "Next: refresh /akk list, then run /akk models before any deliberate set-model attempt.",
|
|
1350
|
+
"This repair never submits a task, chooses a model, approves a prompt, or creates an AKK Turn."
|
|
1351
|
+
].join("\n");
|
|
1352
|
+
}
|
|
1353
|
+
export function isAkkRepairModelControlSuccess(result) {
|
|
1354
|
+
const record = recordValue(result);
|
|
1355
|
+
return record?.outcome === "repaired" || record?.outcome === "already_empty";
|
|
1356
|
+
}
|
|
1357
|
+
export function isAkkSetModelSuccess(result) {
|
|
1358
|
+
const record = recordValue(result);
|
|
1359
|
+
return record?.outcome === "changed" ||
|
|
1360
|
+
record?.outcome === "already_effective";
|
|
1361
|
+
}
|
|
723
1362
|
export function formatAkkThreadTransitionCommandResult(result) {
|
|
724
1363
|
if (!isAkkThreadTransitionSuccess(result)) {
|
|
725
1364
|
const status = nonEmptyString(result.status) ?? "unknown";
|
|
@@ -805,6 +1444,34 @@ export function buildAkkCommandCliArgs(command, config, context = {}) {
|
|
|
805
1444
|
"--terminal",
|
|
806
1445
|
command.terminalId
|
|
807
1446
|
], ["--store-dir", storeDir], ["--codex-home", codexHome], ["--selection-scope", nonEmptyString(context.selectionScope)]);
|
|
1447
|
+
case "model-options":
|
|
1448
|
+
return withOptionalArgs([
|
|
1449
|
+
"model-options",
|
|
1450
|
+
"--terminal",
|
|
1451
|
+
command.terminalId,
|
|
1452
|
+
"--expected-binding-token",
|
|
1453
|
+
requiredExpectedBindingToken(context.expectedBindingToken)
|
|
1454
|
+
], ["--store-dir", storeDir], ["--codex-home", codexHome]);
|
|
1455
|
+
case "repair-model-control":
|
|
1456
|
+
return withOptionalArgs([
|
|
1457
|
+
"repair-model-control",
|
|
1458
|
+
"--terminal",
|
|
1459
|
+
command.terminalId,
|
|
1460
|
+
"--expected-binding-token",
|
|
1461
|
+
requiredExpectedBindingToken(context.expectedBindingToken)
|
|
1462
|
+
], ["--store-dir", storeDir], ["--codex-home", codexHome]);
|
|
1463
|
+
case "set-model":
|
|
1464
|
+
return withOptionalArgs([
|
|
1465
|
+
"set-model",
|
|
1466
|
+
"--terminal",
|
|
1467
|
+
command.terminalId,
|
|
1468
|
+
"--expected-binding-token",
|
|
1469
|
+
requiredExpectedBindingToken(context.expectedBindingToken),
|
|
1470
|
+
"--expected-catalog-fingerprint",
|
|
1471
|
+
requiredExpectedCatalogFingerprint(context.expectedCatalogFingerprint),
|
|
1472
|
+
"--model",
|
|
1473
|
+
command.model
|
|
1474
|
+
], ["--reasoning-effort", command.reasoningEffort], ["--store-dir", storeDir], ["--codex-home", codexHome]);
|
|
808
1475
|
case "new-thread":
|
|
809
1476
|
return withOptionalArgs([
|
|
810
1477
|
"new-thread",
|
|
@@ -944,6 +1611,10 @@ function assertExactTerminalId(value, usage) {
|
|
|
944
1611
|
throw new Error(`${usage}; use the exact terminal_id returned by /akk list`);
|
|
945
1612
|
}
|
|
946
1613
|
}
|
|
1614
|
+
function isAkkModelSemanticIdentifier(value, maxLength) {
|
|
1615
|
+
return value.length <= maxLength &&
|
|
1616
|
+
/^[A-Za-z0-9][A-Za-z0-9._:/+\-]*$/u.test(value);
|
|
1617
|
+
}
|
|
947
1618
|
function isExactNativeThreadId(value) {
|
|
948
1619
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value);
|
|
949
1620
|
}
|
|
@@ -980,6 +1651,13 @@ function requiredCandidateToken(value) {
|
|
|
980
1651
|
}
|
|
981
1652
|
return token;
|
|
982
1653
|
}
|
|
1654
|
+
function requiredExpectedCatalogFingerprint(value) {
|
|
1655
|
+
const fingerprint = nonEmptyString(value);
|
|
1656
|
+
if (!fingerprint) {
|
|
1657
|
+
throw new Error("expected catalog fingerprint is required; refresh model options before switching the model");
|
|
1658
|
+
}
|
|
1659
|
+
return fingerprint;
|
|
1660
|
+
}
|
|
983
1661
|
function requiredSelectionScope(value) {
|
|
984
1662
|
const scope = nonEmptyString(value);
|
|
985
1663
|
if (!scope) {
|
|
@@ -1126,7 +1804,9 @@ function formatAvailableActions(label, resource) {
|
|
|
1126
1804
|
"list_resumable_threads",
|
|
1127
1805
|
"new_thread",
|
|
1128
1806
|
"resume_thread",
|
|
1129
|
-
"native_inspect"
|
|
1807
|
+
"native_inspect",
|
|
1808
|
+
"model_options",
|
|
1809
|
+
"repair_model_control"
|
|
1130
1810
|
]);
|
|
1131
1811
|
const names = Object.keys(actions)
|
|
1132
1812
|
.filter((name) => displayedActions.has(name))
|