@cabane/companion 0.6.178 → 0.6.179
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/cli.js +888 -797
- package/dist/pairing-config.js +2 -0
- package/dist/runtime.js +393 -336
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -1714,16 +1714,49 @@ async function verifyRuntime(state, requestImpl = controlRequest) {
|
|
|
1714
1714
|
return body.instance_id === state.instanceId ? "ours" : "stale";
|
|
1715
1715
|
}
|
|
1716
1716
|
|
|
1717
|
+
// src/heartbeat-status.ts
|
|
1718
|
+
import { z as z3 } from "zod";
|
|
1719
|
+
|
|
1720
|
+
// src/device-state.ts
|
|
1721
|
+
var DEVICE_STATES = ["waiting", "online", "offline", "deactivated"];
|
|
1722
|
+
var HARNESS_STATES = [
|
|
1723
|
+
"checking",
|
|
1724
|
+
"connected",
|
|
1725
|
+
"needs_sign_in",
|
|
1726
|
+
"limited",
|
|
1727
|
+
"problem",
|
|
1728
|
+
"off",
|
|
1729
|
+
"gone"
|
|
1730
|
+
];
|
|
1731
|
+
var DEVICE_HEALTH_STATES = ["checking", "ready", "degraded"];
|
|
1732
|
+
|
|
1733
|
+
// src/heartbeat-status.ts
|
|
1734
|
+
var acknowledgedDeviceSchema = z3.object({
|
|
1735
|
+
state: z3.enum(DEVICE_STATES),
|
|
1736
|
+
health: z3.enum(DEVICE_HEALTH_STATES).nullable(),
|
|
1737
|
+
harnesses: z3.array(z3.object({ runtime: z3.string(), state: z3.enum(HARNESS_STATES) }))
|
|
1738
|
+
});
|
|
1739
|
+
var heartbeatStatusSchema = z3.object({
|
|
1740
|
+
lastHeartbeatAckAt: z3.string().datetime().nullable().default(null),
|
|
1741
|
+
lastAcknowledgedDevice: acknowledgedDeviceSchema.nullable().default(null),
|
|
1742
|
+
lastHeartbeatRefused: z3.boolean().default(false)
|
|
1743
|
+
});
|
|
1744
|
+
var emptyHeartbeatStatus = () => ({
|
|
1745
|
+
lastHeartbeatAckAt: null,
|
|
1746
|
+
lastAcknowledgedDevice: null,
|
|
1747
|
+
lastHeartbeatRefused: false
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1717
1750
|
// src/supervisor.ts
|
|
1718
1751
|
import { hostname as hostname2 } from "os";
|
|
1719
1752
|
|
|
1720
1753
|
// packages/agent-runtime/src/failure.ts
|
|
1721
|
-
import { z as
|
|
1722
|
-
var turnFailureSchema =
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1754
|
+
import { z as z4 } from "zod";
|
|
1755
|
+
var turnFailureSchema = z4.discriminatedUnion("kind", [
|
|
1756
|
+
z4.object({ kind: z4.literal("usage_capped"), resetsAt: z4.string().optional() }),
|
|
1757
|
+
z4.object({ kind: z4.literal("rate_limited") }),
|
|
1758
|
+
z4.object({ kind: z4.literal("server_error") }),
|
|
1759
|
+
z4.object({ kind: z4.literal("auth_expired") })
|
|
1727
1760
|
]);
|
|
1728
1761
|
var USAGE_CAPPED = "usage_capped";
|
|
1729
1762
|
var RATE_LIMITED = "rate_limited";
|
|
@@ -2552,8 +2585,8 @@ import {
|
|
|
2552
2585
|
writeFileSync as writeFileSync3
|
|
2553
2586
|
} from "fs";
|
|
2554
2587
|
import { join as join11 } from "path";
|
|
2555
|
-
import { z as
|
|
2556
|
-
var credentialMapSchema =
|
|
2588
|
+
import { z as z5 } from "zod";
|
|
2589
|
+
var credentialMapSchema = z5.record(z5.string(), z5.string());
|
|
2557
2590
|
function credentialsDir() {
|
|
2558
2591
|
return join11(cabaneDir(), "credentials");
|
|
2559
2592
|
}
|
|
@@ -2769,34 +2802,34 @@ function noResume() {
|
|
|
2769
2802
|
var TURN_PROTOCOL_VERSION = 1;
|
|
2770
2803
|
|
|
2771
2804
|
// packages/agent-runtime/src/host-policy.ts
|
|
2772
|
-
import { z as
|
|
2773
|
-
var hostPolicySchema =
|
|
2805
|
+
import { z as z6 } from "zod";
|
|
2806
|
+
var hostPolicySchema = z6.object({
|
|
2774
2807
|
// Host filesystem and shell: `Bash`/`Read`/`Write`/`Edit`/`Glob`/`Grep`, the notebook pair,
|
|
2775
2808
|
// git-worktree tools and `REPL`. Off on the locked assistant surface; on in the coding posture.
|
|
2776
|
-
hostFs:
|
|
2809
|
+
hostFs: z6.boolean(),
|
|
2777
2810
|
// Public web (`WebSearch` / `WebFetch`). Not host reach; a grant like the rest.
|
|
2778
|
-
web:
|
|
2811
|
+
web: z6.boolean(),
|
|
2779
2812
|
// Browser automation (the Playwright MCP surface). Varies by host.
|
|
2780
|
-
browser:
|
|
2813
|
+
browser: z6.boolean(),
|
|
2781
2814
|
// User-configured MCP servers permitted.
|
|
2782
|
-
userMcp:
|
|
2815
|
+
userMcp: z6.boolean(),
|
|
2783
2816
|
// Subagent dispatch (`Task` / `Agent`) — a grant: `false` on the locked surface, `true` in the
|
|
2784
2817
|
// coding posture, where the coding allow-hook reaches sub-agent tool calls. A subagent completes
|
|
2785
2818
|
// within the turn, so it's not an invariant like `scheduling`.
|
|
2786
|
-
subagents:
|
|
2819
|
+
subagents: z6.boolean(),
|
|
2787
2820
|
// ── Hard platform invariants — always denied, never granted ────────────────
|
|
2788
2821
|
// Deferred re-invocation / scheduling (`ScheduleWakeup`, the `Cron*` / `Task*` families,
|
|
2789
2822
|
// `Monitor`, `Workflow`, …). A turn is one query that resolves on `result`; a scheduled callback
|
|
2790
2823
|
// fires after the reply window has closed and strands the agent.
|
|
2791
|
-
scheduling:
|
|
2824
|
+
scheduling: z6.literal("never"),
|
|
2792
2825
|
// Human-facing UI prompts (`AskUserQuestion`). Nothing in a Cabane conversation can answer a
|
|
2793
2826
|
// structured prompt, so the call hangs the turn. The agent asks in its reply instead.
|
|
2794
|
-
uiPrompts:
|
|
2827
|
+
uiPrompts: z6.literal("never")
|
|
2795
2828
|
});
|
|
2796
2829
|
|
|
2797
2830
|
// packages/agent-runtime/src/turn-event.ts
|
|
2798
|
-
import { z as
|
|
2799
|
-
var turnEventSchema =
|
|
2831
|
+
import { z as z7 } from "zod";
|
|
2832
|
+
var turnEventSchema = z7.discriminatedUnion("type", [
|
|
2800
2833
|
// The runtime's opaque session state, emitted when the adapter learns it (e.g. the SDK
|
|
2801
2834
|
// `system/init` frame). The platform stores `state` verbatim per (conversation, agent) and
|
|
2802
2835
|
// hands it back next turn; only the adapter knows what it means.
|
|
@@ -2808,21 +2841,21 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
2808
2841
|
// server rewinds the mark, so the next turn rebuilds a full catch-up (this turn is unavoidably
|
|
2809
2842
|
// lossy — the degrade is known only after the manifest was committed). A plain boolean; the
|
|
2810
2843
|
// runtime-specific reason stays in the adapter's `onWarn` log. Optional.
|
|
2811
|
-
|
|
2812
|
-
type:
|
|
2813
|
-
state:
|
|
2814
|
-
degraded:
|
|
2844
|
+
z7.object({
|
|
2845
|
+
type: z7.literal("session"),
|
|
2846
|
+
state: z7.string(),
|
|
2847
|
+
degraded: z7.boolean().optional()
|
|
2815
2848
|
}),
|
|
2816
2849
|
// One readable thinking summary. Transient — surfaced live, never persisted as content.
|
|
2817
|
-
|
|
2850
|
+
z7.object({ type: z7.literal("thinking"), text: z7.string() }),
|
|
2818
2851
|
// Assistant text. `terminal: false` is interim narration (commits as `progress`);
|
|
2819
2852
|
// `terminal: true` is the turn's closing reply (commits as `final`).
|
|
2820
|
-
|
|
2853
|
+
z7.object({ type: z7.literal("text"), body: z7.string(), terminal: z7.boolean() }),
|
|
2821
2854
|
// Runtime/provider-authored prose about the turn — a failure's explanation, or a line the
|
|
2822
2855
|
// harness wrote on a turn it recovered. Not the agent's words: the pump persists it as
|
|
2823
2856
|
// `runtime_notice`, drawn in the SystemNote voice. Adapters emit it only from positive runtime
|
|
2824
2857
|
// evidence; the web never classifies English strings.
|
|
2825
|
-
|
|
2858
|
+
z7.object({ type: z7.literal("runtime_notice"), body: z7.string() }),
|
|
2826
2859
|
// A tool-activity transition. `name` is prefix-stripped (`cabane_read`, not
|
|
2827
2860
|
// `mcp__cabane__cabane_read`); `summary` is the short card label; `phase` pairs `start` with a
|
|
2828
2861
|
// later `done`/`error` on the same `id`.
|
|
@@ -2831,15 +2864,15 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
2831
2864
|
// log are a consumer concern (docs/turn-protocol.md). `mcpServer` is the MCP server the call
|
|
2832
2865
|
// came from (`cabane`, `playwright`, …), derived before `name` dropped the prefix; null for a
|
|
2833
2866
|
// host tool. Optional, read as null when absent.
|
|
2834
|
-
|
|
2835
|
-
type:
|
|
2836
|
-
id:
|
|
2837
|
-
name:
|
|
2838
|
-
phase:
|
|
2839
|
-
summary:
|
|
2840
|
-
input:
|
|
2841
|
-
result:
|
|
2842
|
-
mcpServer:
|
|
2867
|
+
z7.object({
|
|
2868
|
+
type: z7.literal("tool"),
|
|
2869
|
+
id: z7.string(),
|
|
2870
|
+
name: z7.string(),
|
|
2871
|
+
phase: z7.enum(["start", "done", "error"]),
|
|
2872
|
+
summary: z7.string(),
|
|
2873
|
+
input: z7.unknown().optional(),
|
|
2874
|
+
result: z7.unknown().optional(),
|
|
2875
|
+
mcpServer: z7.string().nullable().optional()
|
|
2843
2876
|
}),
|
|
2844
2877
|
// The turn's terminal outcome. `ok:false` carries a machine reason (`no_session`, an error
|
|
2845
2878
|
// code), surfaced by the host's clear-PATCH as `errorReason`.
|
|
@@ -2860,70 +2893,70 @@ var turnEventSchema = z6.discriminatedUnion("type", [
|
|
|
2860
2893
|
// started with); `resolvedConfig` the knobs the harness reported (`{ reasoningEffort }` for
|
|
2861
2894
|
// codex). Known only after the run streams, so they ride this event home and the host relays
|
|
2862
2895
|
// them on settle. A cancel has no result event at all.
|
|
2863
|
-
|
|
2864
|
-
type:
|
|
2865
|
-
ok:
|
|
2866
|
-
reason:
|
|
2867
|
-
usage:
|
|
2868
|
-
inputTokens:
|
|
2869
|
-
outputTokens:
|
|
2870
|
-
cacheReadTokens:
|
|
2871
|
-
cacheCreationTokens:
|
|
2872
|
-
contextTokens:
|
|
2873
|
-
contextWindow:
|
|
2896
|
+
z7.object({
|
|
2897
|
+
type: z7.literal("result"),
|
|
2898
|
+
ok: z7.boolean(),
|
|
2899
|
+
reason: z7.string().optional(),
|
|
2900
|
+
usage: z7.object({
|
|
2901
|
+
inputTokens: z7.number(),
|
|
2902
|
+
outputTokens: z7.number(),
|
|
2903
|
+
cacheReadTokens: z7.number().optional(),
|
|
2904
|
+
cacheCreationTokens: z7.number().optional(),
|
|
2905
|
+
contextTokens: z7.number().optional(),
|
|
2906
|
+
contextWindow: z7.number().optional(),
|
|
2874
2907
|
// The rest of the harness's context reading, when it gave one: where it will compact, its
|
|
2875
2908
|
// `/context` breakdown, and every compaction it reported. Claude Code only.
|
|
2876
|
-
contextCompactAt:
|
|
2877
|
-
contextCategories:
|
|
2878
|
-
|
|
2879
|
-
name:
|
|
2880
|
-
tokens:
|
|
2881
|
-
kind:
|
|
2909
|
+
contextCompactAt: z7.number().optional(),
|
|
2910
|
+
contextCategories: z7.array(
|
|
2911
|
+
z7.object({
|
|
2912
|
+
name: z7.string(),
|
|
2913
|
+
tokens: z7.number(),
|
|
2914
|
+
kind: z7.enum(["used", "free", "buffer", "deferred"])
|
|
2882
2915
|
})
|
|
2883
2916
|
).optional(),
|
|
2884
|
-
contextCompactions:
|
|
2885
|
-
|
|
2886
|
-
trigger:
|
|
2887
|
-
preTokens:
|
|
2888
|
-
postTokens:
|
|
2917
|
+
contextCompactions: z7.array(
|
|
2918
|
+
z7.object({
|
|
2919
|
+
trigger: z7.enum(["manual", "auto"]),
|
|
2920
|
+
preTokens: z7.number(),
|
|
2921
|
+
postTokens: z7.number().optional()
|
|
2889
2922
|
})
|
|
2890
2923
|
).optional()
|
|
2891
2924
|
}).optional(),
|
|
2892
|
-
resolvedModel:
|
|
2893
|
-
resolvedConfig:
|
|
2894
|
-
effort:
|
|
2895
|
-
thinking:
|
|
2896
|
-
reasoningEffort:
|
|
2925
|
+
resolvedModel: z7.string().optional(),
|
|
2926
|
+
resolvedConfig: z7.object({
|
|
2927
|
+
effort: z7.string().optional(),
|
|
2928
|
+
thinking: z7.string().optional(),
|
|
2929
|
+
reasoningEffort: z7.string().optional()
|
|
2897
2930
|
}).optional(),
|
|
2898
2931
|
// The harness-reported MCP inventory from this turn's init frame. Diagnostic only: names,
|
|
2899
2932
|
// statuses and a count — never server definitions, credentials or session ids.
|
|
2900
2933
|
// `initReceived:false` tells a missing init frame from a real empty inventory.
|
|
2901
|
-
mcpInventory:
|
|
2902
|
-
initReceived:
|
|
2903
|
-
servers:
|
|
2904
|
-
toolCount:
|
|
2934
|
+
mcpInventory: z7.object({
|
|
2935
|
+
initReceived: z7.boolean(),
|
|
2936
|
+
servers: z7.array(z7.object({ name: z7.string(), status: z7.string() })),
|
|
2937
|
+
toolCount: z7.number().int().nonnegative()
|
|
2905
2938
|
}).optional()
|
|
2906
2939
|
})
|
|
2907
2940
|
]);
|
|
2908
2941
|
|
|
2909
2942
|
// packages/agent-runtime/src/turn-diagnostics.ts
|
|
2910
|
-
import { z as
|
|
2911
|
-
var turnResultReasonSchema =
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2943
|
+
import { z as z8 } from "zod";
|
|
2944
|
+
var turnResultReasonSchema = z8.discriminatedUnion("kind", [
|
|
2945
|
+
z8.object({ kind: z8.literal("usage_capped"), resetsAt: z8.string().optional() }),
|
|
2946
|
+
z8.object({ kind: z8.literal("rate_limited") }),
|
|
2947
|
+
z8.object({ kind: z8.literal("server_error") }),
|
|
2948
|
+
z8.object({ kind: z8.literal("auth_expired") }),
|
|
2949
|
+
z8.object({ kind: z8.literal("no_result") }),
|
|
2950
|
+
z8.object({ kind: z8.literal("empty_result") }),
|
|
2951
|
+
z8.object({ kind: z8.literal("empty_result_unverified") }),
|
|
2952
|
+
z8.object({ kind: z8.literal("timeout_idle") }),
|
|
2953
|
+
z8.object({ kind: z8.literal("timeout_total") }),
|
|
2954
|
+
z8.object({ kind: z8.literal("cancelled") }),
|
|
2955
|
+
z8.object({ kind: z8.literal("lease_lost") }),
|
|
2956
|
+
z8.object({ kind: z8.literal("skipped") }),
|
|
2957
|
+
z8.object({ kind: z8.literal("session_start_failed") }),
|
|
2958
|
+
z8.object({ kind: z8.literal("workspace_tools_missing") }),
|
|
2959
|
+
z8.object({ kind: z8.literal("runtime_error") })
|
|
2927
2960
|
]);
|
|
2928
2961
|
var turnOutcomes = ["success", "failure", "cancelled", "skipped"];
|
|
2929
2962
|
var turnSessionModes = ["fresh", "resumed", "degraded"];
|
|
@@ -2935,25 +2968,25 @@ var turnFinalSources = [
|
|
|
2935
2968
|
"marker",
|
|
2936
2969
|
"none"
|
|
2937
2970
|
];
|
|
2938
|
-
var turnDiagnosticsSchema =
|
|
2939
|
-
outcome:
|
|
2971
|
+
var turnDiagnosticsSchema = z8.object({
|
|
2972
|
+
outcome: z8.enum(turnOutcomes),
|
|
2940
2973
|
resultReason: turnResultReasonSchema.nullable(),
|
|
2941
|
-
sessionMode:
|
|
2942
|
-
sessionFingerprint:
|
|
2943
|
-
eventCounts:
|
|
2944
|
-
session:
|
|
2945
|
-
text:
|
|
2946
|
-
runtime_notice:
|
|
2947
|
-
thinking:
|
|
2948
|
-
tool:
|
|
2949
|
-
result:
|
|
2974
|
+
sessionMode: z8.enum(turnSessionModes),
|
|
2975
|
+
sessionFingerprint: z8.string().regex(/^[a-f0-9]{16}$/).nullable(),
|
|
2976
|
+
eventCounts: z8.object({
|
|
2977
|
+
session: z8.number().int().nonnegative(),
|
|
2978
|
+
text: z8.number().int().nonnegative(),
|
|
2979
|
+
runtime_notice: z8.number().int().nonnegative(),
|
|
2980
|
+
thinking: z8.number().int().nonnegative(),
|
|
2981
|
+
tool: z8.number().int().nonnegative(),
|
|
2982
|
+
result: z8.number().int().nonnegative()
|
|
2950
2983
|
}),
|
|
2951
|
-
runtimeResultKind:
|
|
2952
|
-
finalSource:
|
|
2953
|
-
mcpInventory:
|
|
2954
|
-
initReceived:
|
|
2955
|
-
servers:
|
|
2956
|
-
toolCount:
|
|
2984
|
+
runtimeResultKind: z8.enum(turnRuntimeResultKinds).nullable(),
|
|
2985
|
+
finalSource: z8.enum(turnFinalSources),
|
|
2986
|
+
mcpInventory: z8.object({
|
|
2987
|
+
initReceived: z8.boolean(),
|
|
2988
|
+
servers: z8.array(z8.object({ name: z8.string(), status: z8.string() })),
|
|
2989
|
+
toolCount: z8.number().int().nonnegative()
|
|
2957
2990
|
}).optional()
|
|
2958
2991
|
});
|
|
2959
2992
|
|
|
@@ -3014,100 +3047,100 @@ function classifyEmptyResult(input) {
|
|
|
3014
3047
|
}
|
|
3015
3048
|
|
|
3016
3049
|
// packages/agent-runtime/src/turn-request.ts
|
|
3017
|
-
import { z as
|
|
3018
|
-
var contentBlockSchema =
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
type:
|
|
3022
|
-
source:
|
|
3050
|
+
import { z as z9 } from "zod";
|
|
3051
|
+
var contentBlockSchema = z9.discriminatedUnion("type", [
|
|
3052
|
+
z9.object({ type: z9.literal("text"), text: z9.string() }),
|
|
3053
|
+
z9.object({
|
|
3054
|
+
type: z9.literal("image"),
|
|
3055
|
+
source: z9.object({ type: z9.literal("url"), url: z9.string() })
|
|
3023
3056
|
}),
|
|
3024
|
-
|
|
3025
|
-
type:
|
|
3026
|
-
source:
|
|
3057
|
+
z9.object({
|
|
3058
|
+
type: z9.literal("document"),
|
|
3059
|
+
source: z9.object({ type: z9.literal("url"), url: z9.string() })
|
|
3027
3060
|
})
|
|
3028
3061
|
]);
|
|
3029
|
-
var effortLevelSchema =
|
|
3030
|
-
var resolvedRunConfigSchema =
|
|
3031
|
-
model:
|
|
3062
|
+
var effortLevelSchema = z9.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
3063
|
+
var resolvedRunConfigSchema = z9.object({
|
|
3064
|
+
model: z9.string().nullable(),
|
|
3032
3065
|
effort: effortLevelSchema.optional(),
|
|
3033
|
-
runtimeOptions:
|
|
3066
|
+
runtimeOptions: z9.record(z9.string(), z9.unknown()).optional()
|
|
3034
3067
|
});
|
|
3035
|
-
var resolvedMcpServerSchema =
|
|
3036
|
-
|
|
3037
|
-
type:
|
|
3038
|
-
command:
|
|
3039
|
-
args:
|
|
3040
|
-
env:
|
|
3041
|
-
envFromEnv:
|
|
3068
|
+
var resolvedMcpServerSchema = z9.union([
|
|
3069
|
+
z9.object({
|
|
3070
|
+
type: z9.literal("stdio").optional(),
|
|
3071
|
+
command: z9.string(),
|
|
3072
|
+
args: z9.array(z9.string()).optional(),
|
|
3073
|
+
env: z9.record(z9.string(), z9.string()).optional(),
|
|
3074
|
+
envFromEnv: z9.record(z9.string(), z9.string()).optional()
|
|
3042
3075
|
}),
|
|
3043
|
-
|
|
3044
|
-
type:
|
|
3045
|
-
url:
|
|
3046
|
-
headers:
|
|
3047
|
-
headersFromEnv:
|
|
3076
|
+
z9.object({
|
|
3077
|
+
type: z9.literal("http"),
|
|
3078
|
+
url: z9.string(),
|
|
3079
|
+
headers: z9.record(z9.string(), z9.string()).optional(),
|
|
3080
|
+
headersFromEnv: z9.record(z9.string(), z9.string()).optional()
|
|
3048
3081
|
}),
|
|
3049
|
-
|
|
3050
|
-
type:
|
|
3051
|
-
url:
|
|
3052
|
-
headers:
|
|
3053
|
-
headersFromEnv:
|
|
3082
|
+
z9.object({
|
|
3083
|
+
type: z9.literal("sse"),
|
|
3084
|
+
url: z9.string(),
|
|
3085
|
+
headers: z9.record(z9.string(), z9.string()).optional(),
|
|
3086
|
+
headersFromEnv: z9.record(z9.string(), z9.string()).optional()
|
|
3054
3087
|
})
|
|
3055
3088
|
]);
|
|
3056
|
-
var resolvedMcpServersSchema =
|
|
3057
|
-
var hostInjectedServersSchema =
|
|
3058
|
-
var turnRequestSchema =
|
|
3089
|
+
var resolvedMcpServersSchema = z9.record(z9.string(), resolvedMcpServerSchema);
|
|
3090
|
+
var hostInjectedServersSchema = z9.record(z9.string(), z9.unknown());
|
|
3091
|
+
var turnRequestSchema = z9.object({
|
|
3059
3092
|
// Server-composed standing instructions: the core segment (body, tool naming, identity, agent
|
|
3060
3093
|
// charter) and, after it, the conversation segment (workspace and room charters, skill indexes,
|
|
3061
3094
|
// roster). One string to the adapter, current as of this dispatch.
|
|
3062
|
-
systemPrompt:
|
|
3095
|
+
systemPrompt: z9.string(),
|
|
3063
3096
|
// The same text as its two ordered segments, so an adapter that writes standing text into the
|
|
3064
3097
|
// conversation (Codex's input preamble) can tell which half changed. `systemPrompt` is
|
|
3065
3098
|
// `joinStanding(standing)`. Optional: absent from an API older than the split, where
|
|
3066
3099
|
// `systemPrompt` is the core alone and the conversation material rides `prompt`.
|
|
3067
|
-
standing:
|
|
3100
|
+
standing: z9.object({ core: z9.string(), conversation: z9.string() }).optional(),
|
|
3068
3101
|
// Server-composed per-turn user text (anchor reminder + the triggering message).
|
|
3069
|
-
prompt:
|
|
3102
|
+
prompt: z9.string(),
|
|
3070
3103
|
// The multi-block user-message body (text + vision).
|
|
3071
|
-
content:
|
|
3104
|
+
content: z9.array(contentBlockSchema),
|
|
3072
3105
|
// Portable-or-dialect run-config (above).
|
|
3073
3106
|
config: resolvedRunConfigSchema,
|
|
3074
3107
|
// Abstract capability grants; the adapter maps them to tool names.
|
|
3075
3108
|
policy: hostPolicySchema,
|
|
3076
3109
|
// Prior opaque session state, or null for a fresh session.
|
|
3077
|
-
session:
|
|
3110
|
+
session: z9.string().nullable(),
|
|
3078
3111
|
// The cabane control-plane coordinates for this turn's MCP + post-back.
|
|
3079
|
-
cabane:
|
|
3080
|
-
mcpUrl:
|
|
3081
|
-
bearer:
|
|
3082
|
-
activeConversationId:
|
|
3112
|
+
cabane: z9.object({
|
|
3113
|
+
mcpUrl: z9.string(),
|
|
3114
|
+
bearer: z9.string(),
|
|
3115
|
+
activeConversationId: z9.string(),
|
|
3083
3116
|
// Scoped turn-control MCP URL. Codex and opencode mount it as `cabane_companion` with the bearer
|
|
3084
3117
|
// and active-conversation header, for ask, withdraw_ask, wake_me, cancel_wake and skip_turn.
|
|
3085
3118
|
// Claude Code mounts the companion's in-process instance instead. Optional; the companion
|
|
3086
3119
|
// populates it.
|
|
3087
|
-
turnControlUrl:
|
|
3120
|
+
turnControlUrl: z9.string().optional(),
|
|
3088
3121
|
// Workspace coordinate retained for envelope compatibility. Current
|
|
3089
3122
|
// adapters use MCP, whose workspace operations take workspaceId explicitly.
|
|
3090
3123
|
// The companion supplies this field; it is not required by those adapters.
|
|
3091
|
-
workspaceId:
|
|
3124
|
+
workspaceId: z9.string().optional()
|
|
3092
3125
|
}),
|
|
3093
3126
|
// Machine-local resolution (host-filled): the checkout cwd, extra env from a
|
|
3094
3127
|
// prepare hook, and the resolved user MCP servers.
|
|
3095
|
-
local:
|
|
3096
|
-
cwd:
|
|
3097
|
-
env:
|
|
3128
|
+
local: z9.object({
|
|
3129
|
+
cwd: z9.string().optional(),
|
|
3130
|
+
env: z9.record(z9.string(), z9.string()).optional(),
|
|
3098
3131
|
mcpServers: resolvedMcpServersSchema.optional(),
|
|
3099
3132
|
// The values `headersFromEnv` names, per turn (`CABANE_MCP_<n>` → complete header value). The
|
|
3100
3133
|
// adapter merges them into the harness process's environment and nowhere else; the host never
|
|
3101
3134
|
// merges them into its own. Absent when no server references one.
|
|
3102
|
-
secretEnv:
|
|
3135
|
+
secretEnv: z9.record(z9.string(), z9.string()).optional(),
|
|
3103
3136
|
// Machine-local claude-code knobs for an operator running their own companion — the auto-memory
|
|
3104
3137
|
// escape hatch. `autoMemory: true` opts back into Claude Code's auto-memory (governed by the
|
|
3105
3138
|
// operator's `.claude/settings.json`); absent/false keeps the adapter's force-off default
|
|
3106
3139
|
// (`buildClaudeCodeOptions`).
|
|
3107
|
-
claudeCode:
|
|
3140
|
+
claudeCode: z9.object({ autoMemory: z9.boolean().optional() }).optional()
|
|
3108
3141
|
}),
|
|
3109
3142
|
// Host-owned injected servers (host-filled).
|
|
3110
|
-
extra:
|
|
3143
|
+
extra: z9.object({
|
|
3111
3144
|
mcpServers: hostInjectedServersSchema
|
|
3112
3145
|
})
|
|
3113
3146
|
});
|
|
@@ -3123,12 +3156,12 @@ function standingSegments(req) {
|
|
|
3123
3156
|
}
|
|
3124
3157
|
|
|
3125
3158
|
// packages/agent-runtime/src/conformance.ts
|
|
3126
|
-
import { z as
|
|
3127
|
-
var conformanceFixtureSchema =
|
|
3128
|
-
name:
|
|
3159
|
+
import { z as z10 } from "zod";
|
|
3160
|
+
var conformanceFixtureSchema = z10.object({
|
|
3161
|
+
name: z10.string(),
|
|
3129
3162
|
request: turnRequestSchema,
|
|
3130
|
-
nativeStream:
|
|
3131
|
-
expected:
|
|
3163
|
+
nativeStream: z10.array(z10.unknown()),
|
|
3164
|
+
expected: z10.array(turnEventSchema)
|
|
3132
3165
|
});
|
|
3133
3166
|
|
|
3134
3167
|
// packages/agent-runtime/src/transcript.ts
|
|
@@ -3583,7 +3616,7 @@ function envResolvedFrom(server, secretEnv) {
|
|
|
3583
3616
|
}
|
|
3584
3617
|
|
|
3585
3618
|
// packages/agent-runtime/src/claude-code/policy.ts
|
|
3586
|
-
import { z as
|
|
3619
|
+
import { z as z11 } from "zod";
|
|
3587
3620
|
var HOST_FS_TOOLS = [
|
|
3588
3621
|
// shell + local filesystem
|
|
3589
3622
|
"Bash",
|
|
@@ -3633,18 +3666,18 @@ function withThinkingSummaries(thinking) {
|
|
|
3633
3666
|
if (thinking.type === "disabled") return thinking;
|
|
3634
3667
|
return { display: "summarized", ...thinking };
|
|
3635
3668
|
}
|
|
3636
|
-
var claudeCodeDialectSchema =
|
|
3637
|
-
thinking:
|
|
3638
|
-
|
|
3639
|
-
type:
|
|
3640
|
-
display:
|
|
3669
|
+
var claudeCodeDialectSchema = z11.object({
|
|
3670
|
+
thinking: z11.discriminatedUnion("type", [
|
|
3671
|
+
z11.object({
|
|
3672
|
+
type: z11.literal("adaptive"),
|
|
3673
|
+
display: z11.enum(["summarized", "omitted"]).optional()
|
|
3641
3674
|
}),
|
|
3642
|
-
|
|
3643
|
-
type:
|
|
3644
|
-
budgetTokens:
|
|
3645
|
-
display:
|
|
3675
|
+
z11.object({
|
|
3676
|
+
type: z11.literal("enabled"),
|
|
3677
|
+
budgetTokens: z11.number().int().positive().optional(),
|
|
3678
|
+
display: z11.enum(["summarized", "omitted"]).optional()
|
|
3646
3679
|
}),
|
|
3647
|
-
|
|
3680
|
+
z11.object({ type: z11.literal("disabled") })
|
|
3648
3681
|
]).optional()
|
|
3649
3682
|
}).loose();
|
|
3650
3683
|
function readThinking(runtimeOptions) {
|
|
@@ -5113,7 +5146,7 @@ function sealHeld(held, terminal) {
|
|
|
5113
5146
|
}
|
|
5114
5147
|
|
|
5115
5148
|
// packages/agent-runtime/src/opencode/policy.ts
|
|
5116
|
-
import { z as
|
|
5149
|
+
import { z as z12 } from "zod";
|
|
5117
5150
|
var OPENCODE_HOST_TOOLS = [
|
|
5118
5151
|
"bash",
|
|
5119
5152
|
"edit",
|
|
@@ -5140,8 +5173,8 @@ function opencodeToolPolicy(policy) {
|
|
|
5140
5173
|
deny(OPENCODE_UI_PROMPT_TOOLS);
|
|
5141
5174
|
return { tools, allowAllHostTools: policy.hostFs };
|
|
5142
5175
|
}
|
|
5143
|
-
var opencodeDialectSchema =
|
|
5144
|
-
agent:
|
|
5176
|
+
var opencodeDialectSchema = z12.object({
|
|
5177
|
+
agent: z12.string().min(1).optional()
|
|
5145
5178
|
}).loose();
|
|
5146
5179
|
function readOpencodeDialect(runtimeOptions) {
|
|
5147
5180
|
const parsed = opencodeDialectSchema.safeParse(runtimeOptions?.["opencode"] ?? {});
|
|
@@ -6355,7 +6388,7 @@ function sealHeld2(held, terminal) {
|
|
|
6355
6388
|
}
|
|
6356
6389
|
|
|
6357
6390
|
// packages/agent-runtime/src/codex/policy.ts
|
|
6358
|
-
import { z as
|
|
6391
|
+
import { z as z13 } from "zod";
|
|
6359
6392
|
function codexToolPolicy(policy) {
|
|
6360
6393
|
return policy.hostFs ? {
|
|
6361
6394
|
sandboxMode: "danger-full-access",
|
|
@@ -6374,8 +6407,8 @@ function codexToolPolicy(policy) {
|
|
|
6374
6407
|
};
|
|
6375
6408
|
}
|
|
6376
6409
|
var CODEX_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
|
|
6377
|
-
var codexDialectSchema =
|
|
6378
|
-
modelReasoningEffort:
|
|
6410
|
+
var codexDialectSchema = z13.object({
|
|
6411
|
+
modelReasoningEffort: z13.enum(CODEX_REASONING_EFFORTS).optional()
|
|
6379
6412
|
}).loose();
|
|
6380
6413
|
function readCodexDialect(runtimeOptions) {
|
|
6381
6414
|
const parsed = codexDialectSchema.safeParse(runtimeOptions?.["codex"] ?? {});
|
|
@@ -7817,30 +7850,30 @@ import { createInterface } from "readline";
|
|
|
7817
7850
|
// src/runner/sandbox-policy.ts
|
|
7818
7851
|
import { NetworkConfigSchema } from "@anthropic-ai/sandbox-runtime";
|
|
7819
7852
|
import { globToRegex } from "@anthropic-ai/sandbox-runtime/dist/sandbox/sandbox-utils.js";
|
|
7820
|
-
import { z as
|
|
7821
|
-
var hostPattern =
|
|
7822
|
-
var secretRef =
|
|
7823
|
-
var envCredentialSchema =
|
|
7824
|
-
var gitCredentialSchema =
|
|
7825
|
-
host:
|
|
7826
|
-
username:
|
|
7853
|
+
import { z as z14 } from "zod";
|
|
7854
|
+
var hostPattern = z14.string().min(1);
|
|
7855
|
+
var secretRef = z14.string().regex(/^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/, "must be a ${NAME} reference into secrets.json");
|
|
7856
|
+
var envCredentialSchema = z14.object({ secret: secretRef, hosts: z14.array(hostPattern).min(1) }).strict();
|
|
7857
|
+
var gitCredentialSchema = z14.object({
|
|
7858
|
+
host: z14.string().regex(/^[A-Za-z0-9.-]+(:\d+)?$/, "must be a host name, like github.com"),
|
|
7859
|
+
username: z14.string().min(1).optional(),
|
|
7827
7860
|
secret: secretRef
|
|
7828
7861
|
}).strict();
|
|
7829
|
-
var sandboxPolicySchema =
|
|
7830
|
-
hosts:
|
|
7831
|
-
allow:
|
|
7832
|
-
deny:
|
|
7833
|
-
defaults:
|
|
7862
|
+
var sandboxPolicySchema = z14.object({
|
|
7863
|
+
hosts: z14.object({
|
|
7864
|
+
allow: z14.array(hostPattern).optional(),
|
|
7865
|
+
deny: z14.array(hostPattern).optional(),
|
|
7866
|
+
defaults: z14.boolean().optional()
|
|
7834
7867
|
}).strict().optional(),
|
|
7835
|
-
filesystem:
|
|
7836
|
-
mode:
|
|
7837
|
-
denyRead:
|
|
7838
|
-
allowRead:
|
|
7839
|
-
allowWrite:
|
|
7868
|
+
filesystem: z14.object({
|
|
7869
|
+
mode: z14.enum(["blocklist", "strict"]).optional(),
|
|
7870
|
+
denyRead: z14.array(z14.string().min(1)).optional(),
|
|
7871
|
+
allowRead: z14.array(z14.string().min(1)).optional(),
|
|
7872
|
+
allowWrite: z14.array(z14.string().min(1)).optional()
|
|
7840
7873
|
}).strict().optional(),
|
|
7841
|
-
credentials:
|
|
7842
|
-
env:
|
|
7843
|
-
git:
|
|
7874
|
+
credentials: z14.object({
|
|
7875
|
+
env: z14.record(z14.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), envCredentialSchema).optional(),
|
|
7876
|
+
git: z14.array(gitCredentialSchema).optional()
|
|
7844
7877
|
}).strict().optional()
|
|
7845
7878
|
}).strict();
|
|
7846
7879
|
function parseSandboxPolicy(raw) {
|
|
@@ -8269,12 +8302,12 @@ function createTurnContainment(turnId, deps = {}) {
|
|
|
8269
8302
|
// src/secrets.ts
|
|
8270
8303
|
import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
|
|
8271
8304
|
import { join as join13 } from "path";
|
|
8272
|
-
import { z as
|
|
8305
|
+
import { z as z15 } from "zod";
|
|
8273
8306
|
var PLACEHOLDER_RE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
8274
8307
|
function secretsPath() {
|
|
8275
8308
|
return join13(cabaneDir(), "secrets.json");
|
|
8276
8309
|
}
|
|
8277
|
-
var secretStoreSchema =
|
|
8310
|
+
var secretStoreSchema = z15.record(z15.string(), z15.string());
|
|
8278
8311
|
function loadSecretStore() {
|
|
8279
8312
|
const path = secretsPath();
|
|
8280
8313
|
if (!existsSync9(path)) return makeStore({});
|
|
@@ -8391,52 +8424,52 @@ function missingSecretReason(missing) {
|
|
|
8391
8424
|
}
|
|
8392
8425
|
|
|
8393
8426
|
// src/runner/protocol.ts
|
|
8394
|
-
import { z as
|
|
8427
|
+
import { z as z17 } from "zod";
|
|
8395
8428
|
|
|
8396
8429
|
// src/run-config.ts
|
|
8397
|
-
import { z as
|
|
8398
|
-
var mcpStdioServerSchema =
|
|
8399
|
-
type:
|
|
8400
|
-
command:
|
|
8401
|
-
args:
|
|
8402
|
-
env:
|
|
8430
|
+
import { z as z16 } from "zod";
|
|
8431
|
+
var mcpStdioServerSchema = z16.object({
|
|
8432
|
+
type: z16.literal("stdio").optional(),
|
|
8433
|
+
command: z16.string().min(1),
|
|
8434
|
+
args: z16.array(z16.string()).optional(),
|
|
8435
|
+
env: z16.record(z16.string(), z16.string()).optional(),
|
|
8403
8436
|
// Variable → the name of a `secretEnv` entry holding its value. Set by the server on a
|
|
8404
8437
|
// room's shared browser, whose connect URL is a credential.
|
|
8405
|
-
envFromEnv:
|
|
8438
|
+
envFromEnv: z16.record(z16.string(), z16.string()).optional()
|
|
8406
8439
|
});
|
|
8407
|
-
var mcpHttpServerSchema =
|
|
8408
|
-
type:
|
|
8409
|
-
url:
|
|
8410
|
-
headers:
|
|
8440
|
+
var mcpHttpServerSchema = z16.object({
|
|
8441
|
+
type: z16.literal("http"),
|
|
8442
|
+
url: z16.string().url(),
|
|
8443
|
+
headers: z16.record(z16.string(), z16.string()).optional(),
|
|
8411
8444
|
// Header → the name of a `secretEnv` entry holding its value. Set by
|
|
8412
8445
|
// the server on a room connection's entry, and by `resolveMcpSecrets` on a
|
|
8413
8446
|
// header whose `${VAR}` it resolved.
|
|
8414
|
-
headersFromEnv:
|
|
8447
|
+
headersFromEnv: z16.record(z16.string(), z16.string()).optional()
|
|
8415
8448
|
});
|
|
8416
|
-
var mcpSseServerSchema =
|
|
8417
|
-
type:
|
|
8418
|
-
url:
|
|
8419
|
-
headers:
|
|
8449
|
+
var mcpSseServerSchema = z16.object({
|
|
8450
|
+
type: z16.literal("sse"),
|
|
8451
|
+
url: z16.string().url(),
|
|
8452
|
+
headers: z16.record(z16.string(), z16.string()).optional(),
|
|
8420
8453
|
// Header → the name of a `secretEnv` entry holding its value. Set by
|
|
8421
8454
|
// the server on a room connection's entry, and by `resolveMcpSecrets` on a
|
|
8422
8455
|
// header whose `${VAR}` it resolved.
|
|
8423
|
-
headersFromEnv:
|
|
8456
|
+
headersFromEnv: z16.record(z16.string(), z16.string()).optional()
|
|
8424
8457
|
});
|
|
8425
|
-
var mcpServerDefSchema =
|
|
8426
|
-
var thinkingConfigSchema =
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8458
|
+
var mcpServerDefSchema = z16.union([mcpHttpServerSchema, mcpSseServerSchema, mcpStdioServerSchema]);
|
|
8459
|
+
var thinkingConfigSchema = z16.discriminatedUnion("type", [
|
|
8460
|
+
z16.object({ type: z16.literal("adaptive") }),
|
|
8461
|
+
z16.object({ type: z16.literal("enabled"), budgetTokens: z16.number().int().positive().optional() }),
|
|
8462
|
+
z16.object({ type: z16.literal("disabled") })
|
|
8430
8463
|
]);
|
|
8431
|
-
var effortSchema =
|
|
8432
|
-
var runConfigSchema =
|
|
8464
|
+
var effortSchema = z16.enum(["low", "medium", "high", "xhigh", "max"]);
|
|
8465
|
+
var runConfigSchema = z16.object({
|
|
8433
8466
|
// `true` grants the host filesystem/shell; absent is the locked surface. Kept
|
|
8434
8467
|
// in lockstep with `@cabane/shared`'s
|
|
8435
8468
|
// `agentRunConfigSchema` (independent zod, forward-compatible: unknown keys are
|
|
8436
8469
|
// stripped, so an older companion riding a newer server never rejects the config).
|
|
8437
|
-
hostAccess:
|
|
8438
|
-
mcpServers:
|
|
8439
|
-
model:
|
|
8470
|
+
hostAccess: z16.boolean().optional(),
|
|
8471
|
+
mcpServers: z16.record(z16.string(), mcpServerDefSchema).optional(),
|
|
8472
|
+
model: z16.string().min(1).optional(),
|
|
8440
8473
|
thinking: thinkingConfigSchema.optional(),
|
|
8441
8474
|
effort: effortSchema.optional()
|
|
8442
8475
|
});
|
|
@@ -8464,76 +8497,76 @@ var RUNNER_ENV = {
|
|
|
8464
8497
|
// Everything else the turn is constructed from, as JSON: see `RunnerSpec`.
|
|
8465
8498
|
spec: "CABANE_RUNNER_SPEC"
|
|
8466
8499
|
};
|
|
8467
|
-
var runtimeHealthSchema =
|
|
8468
|
-
quotaState:
|
|
8469
|
-
limitedUntil:
|
|
8470
|
-
lastError:
|
|
8500
|
+
var runtimeHealthSchema = z17.object({
|
|
8501
|
+
quotaState: z17.enum(["ok", "limited", "auth_expired", "unknown"]),
|
|
8502
|
+
limitedUntil: z17.string().nullable(),
|
|
8503
|
+
lastError: z17.string().nullable()
|
|
8471
8504
|
});
|
|
8472
|
-
var runnerSpecSchema =
|
|
8473
|
-
deviceId:
|
|
8505
|
+
var runnerSpecSchema = z17.object({
|
|
8506
|
+
deviceId: z17.string().optional(),
|
|
8474
8507
|
// The launcher's boot id: the one its heartbeat carries, so a runner's lease
|
|
8475
8508
|
// matches the device that holds it.
|
|
8476
|
-
bootId:
|
|
8477
|
-
workspaceId:
|
|
8478
|
-
workspaceSlug:
|
|
8479
|
-
agentId:
|
|
8480
|
-
agentUsername:
|
|
8481
|
-
agentName:
|
|
8509
|
+
bootId: z17.string(),
|
|
8510
|
+
workspaceId: z17.string(),
|
|
8511
|
+
workspaceSlug: z17.string(),
|
|
8512
|
+
agentId: z17.string(),
|
|
8513
|
+
agentUsername: z17.string(),
|
|
8514
|
+
agentName: z17.string(),
|
|
8482
8515
|
local: localAgentConfigSchema,
|
|
8483
8516
|
runConfig: runConfigSchema,
|
|
8484
|
-
opencodeServerUrl:
|
|
8485
|
-
claudeCodeAvailable:
|
|
8486
|
-
codexAvailable:
|
|
8487
|
-
idleTimeoutMs:
|
|
8488
|
-
totalTimeoutMs:
|
|
8489
|
-
leaseRenewalMs:
|
|
8490
|
-
preparingRowDelayMs:
|
|
8517
|
+
opencodeServerUrl: z17.string().optional(),
|
|
8518
|
+
claudeCodeAvailable: z17.boolean(),
|
|
8519
|
+
codexAvailable: z17.boolean(),
|
|
8520
|
+
idleTimeoutMs: z17.number().int().positive().optional(),
|
|
8521
|
+
totalTimeoutMs: z17.number().int().positive().optional(),
|
|
8522
|
+
leaseRenewalMs: z17.number().int().positive().optional(),
|
|
8523
|
+
preparingRowDelayMs: z17.number().int().nonnegative().optional(),
|
|
8491
8524
|
// Whether the turn writes a transcript. The directory is the runner's own
|
|
8492
8525
|
// (`transcriptsDir()`), so `CABANE_RUNNER_STATE_DIR` moves it with the outbox.
|
|
8493
|
-
transcripts:
|
|
8526
|
+
transcripts: z17.boolean(),
|
|
8494
8527
|
// Whether the harness starts under the OS sandbox. The launcher sends 'os'
|
|
8495
8528
|
// only when this host can make one.
|
|
8496
|
-
sandbox:
|
|
8529
|
+
sandbox: z17.enum(["os", "none"]),
|
|
8497
8530
|
// The device's sandbox policy, validated by the launcher. Absent
|
|
8498
8531
|
// when the sandbox is off.
|
|
8499
8532
|
sandboxPolicy: sandboxPolicySchema.optional(),
|
|
8500
8533
|
// Every companion store the launcher's latest walk found, denied
|
|
8501
8534
|
// to the harness. Absent when the sandbox is off.
|
|
8502
|
-
companionStores:
|
|
8503
|
-
logLevel:
|
|
8535
|
+
companionStores: z17.array(z17.string()).optional(),
|
|
8536
|
+
logLevel: z17.string(),
|
|
8504
8537
|
// The launcher's connector-health observations, by runtime, as of the start.
|
|
8505
|
-
connectorHealth:
|
|
8538
|
+
connectorHealth: z17.record(z17.string(), runtimeHealthSchema),
|
|
8506
8539
|
// True when this (conversation, agent) pair was already told its session
|
|
8507
8540
|
// could not be saved: the notice is once per pair, not once per process.
|
|
8508
|
-
sessionWriteNoticeClaimed:
|
|
8541
|
+
sessionWriteNoticeClaimed: z17.boolean(),
|
|
8509
8542
|
// The runner's identity, minted with its credential. The start
|
|
8510
8543
|
// PATCH carries it so the lease knows which runner holds the span.
|
|
8511
|
-
runnerId:
|
|
8544
|
+
runnerId: z17.string(),
|
|
8512
8545
|
// The launcher keeps the runtime's session files beside the attempt
|
|
8513
8546
|
// and restores them into this runner's home, so the runner names the attempt
|
|
8514
8547
|
// each context was composed for and waits for the launcher to say it's in
|
|
8515
8548
|
// place. Absent: nothing to wait for (a runner on the launcher's own disk).
|
|
8516
|
-
attemptState:
|
|
8549
|
+
attemptState: z17.boolean().optional(),
|
|
8517
8550
|
// MCP `${NAME}` secrets come from the launcher, by name, instead of
|
|
8518
8551
|
// this machine's secrets.json. Absent: the runner reads the device's file.
|
|
8519
|
-
secretsFromLauncher:
|
|
8552
|
+
secretsFromLauncher: z17.boolean().optional()
|
|
8520
8553
|
});
|
|
8521
|
-
var dispatchSchema =
|
|
8522
|
-
type:
|
|
8523
|
-
agentId:
|
|
8524
|
-
deviceId:
|
|
8525
|
-
conversationId:
|
|
8526
|
-
messageId:
|
|
8527
|
-
episode:
|
|
8554
|
+
var dispatchSchema = z17.object({
|
|
8555
|
+
type: z17.literal("device:dispatch_requested"),
|
|
8556
|
+
agentId: z17.string(),
|
|
8557
|
+
deviceId: z17.string(),
|
|
8558
|
+
conversationId: z17.string(),
|
|
8559
|
+
messageId: z17.string(),
|
|
8560
|
+
episode: z17.number().int().optional()
|
|
8528
8561
|
});
|
|
8529
|
-
var credentialsSchema =
|
|
8530
|
-
credential:
|
|
8531
|
-
sandboxCredentials:
|
|
8532
|
-
|
|
8533
|
-
name:
|
|
8534
|
-
value:
|
|
8535
|
-
hosts:
|
|
8536
|
-
gitHost:
|
|
8562
|
+
var credentialsSchema = z17.object({
|
|
8563
|
+
credential: z17.string().startsWith("cabrn_"),
|
|
8564
|
+
sandboxCredentials: z17.array(
|
|
8565
|
+
z17.object({
|
|
8566
|
+
name: z17.string(),
|
|
8567
|
+
value: z17.string(),
|
|
8568
|
+
hosts: z17.array(z17.string()),
|
|
8569
|
+
gitHost: z17.string().optional()
|
|
8537
8570
|
})
|
|
8538
8571
|
).optional()
|
|
8539
8572
|
});
|
|
@@ -9002,7 +9035,7 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
9002
9035
|
|
|
9003
9036
|
// src/turn-control-tools.ts
|
|
9004
9037
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9005
|
-
import { z as
|
|
9038
|
+
import { z as z18 } from "zod";
|
|
9006
9039
|
var COMPANION_LOCAL_MCP_SERVER = "cabane_companion";
|
|
9007
9040
|
var COMPANION_LOCAL_TOOL_GLOB = `mcp__${COMPANION_LOCAL_MCP_SERVER}__*`;
|
|
9008
9041
|
var SKIP_TURN_TOOL = "skip_turn";
|
|
@@ -9065,7 +9098,7 @@ function createTurnControlMcpServer(acts, skipState) {
|
|
|
9065
9098
|
SKIP_TURN_TOOL,
|
|
9066
9099
|
TURN_CONTROL_TOOL_DESCRIPTIONS.skip_turn,
|
|
9067
9100
|
{
|
|
9068
|
-
reason:
|
|
9101
|
+
reason: z18.string().min(1).max(MAX_SKIP_REASON_BYTES).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.skip_turn.reason)
|
|
9069
9102
|
},
|
|
9070
9103
|
async (args) => {
|
|
9071
9104
|
skipState.skipped = true;
|
|
@@ -9081,22 +9114,22 @@ function createTurnControlMcpServer(acts, skipState) {
|
|
|
9081
9114
|
ASK_TOOL,
|
|
9082
9115
|
TURN_CONTROL_TOOL_DESCRIPTIONS.ask,
|
|
9083
9116
|
{
|
|
9084
|
-
targetUserId:
|
|
9085
|
-
question:
|
|
9086
|
-
headline:
|
|
9087
|
-
options:
|
|
9088
|
-
questions:
|
|
9089
|
-
|
|
9090
|
-
headline:
|
|
9091
|
-
body:
|
|
9092
|
-
options:
|
|
9093
|
-
|
|
9117
|
+
targetUserId: z18.string().regex(ASK_TARGET_PATTERN, { error: ASK_TARGET_INVALID }).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.targetUserId),
|
|
9118
|
+
question: z18.string().min(1).max(MAX_ASK_QUESTION_BYTES).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.question),
|
|
9119
|
+
headline: z18.string().min(1).max(MAX_ASK_HEADLINE_BYTES).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.headline),
|
|
9120
|
+
options: z18.array(z18.string({ error: ASK_OPTION_NOT_A_STRING }).min(1).max(MAX_ASK_OPTION_BYTES)).min(MIN_ASK_OPTIONS).max(MAX_ASK_OPTIONS).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.options),
|
|
9121
|
+
questions: z18.array(
|
|
9122
|
+
z18.object({
|
|
9123
|
+
headline: z18.string().min(1).max(MAX_ASK_HEADLINE_BYTES).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask["questions.headline"]),
|
|
9124
|
+
body: z18.string().min(1).max(MAX_ASK_QUESTION_BYTES).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask["questions.body"]),
|
|
9125
|
+
options: z18.array(
|
|
9126
|
+
z18.string({ error: ASK_OPTION_NOT_A_STRING }).min(1).max(MAX_ASK_OPTION_BYTES)
|
|
9094
9127
|
).min(MIN_ASK_OPTIONS).max(MAX_ASK_OPTIONS).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask["questions.options"])
|
|
9095
9128
|
})
|
|
9096
9129
|
).min(1).max(MAX_ASK_ITEMS).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.questions),
|
|
9097
|
-
replaces:
|
|
9098
|
-
askId:
|
|
9099
|
-
reason:
|
|
9130
|
+
replaces: z18.object({
|
|
9131
|
+
askId: z18.string().uuid().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask["replaces.askId"]),
|
|
9132
|
+
reason: z18.string().min(1).max(MAX_WITHDRAW_REASON_BYTES).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask["replaces.reason"])
|
|
9100
9133
|
}).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.ask.replaces)
|
|
9101
9134
|
},
|
|
9102
9135
|
async (args) => {
|
|
@@ -9137,9 +9170,9 @@ function createTurnControlMcpServer(acts, skipState) {
|
|
|
9137
9170
|
WAKE_ME_TOOL,
|
|
9138
9171
|
TURN_CONTROL_TOOL_DESCRIPTIONS.wake_me,
|
|
9139
9172
|
{
|
|
9140
|
-
afterSeconds:
|
|
9141
|
-
at:
|
|
9142
|
-
note:
|
|
9173
|
+
afterSeconds: z18.number().int().positive().optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.wake_me.afterSeconds),
|
|
9174
|
+
at: z18.string().datetime({ offset: true }).optional().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.wake_me.at),
|
|
9175
|
+
note: z18.string().min(1).max(MAX_WAKE_NOTE_BYTES).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.wake_me.note)
|
|
9143
9176
|
},
|
|
9144
9177
|
async (args) => {
|
|
9145
9178
|
const hasAfter = args.afterSeconds !== void 0;
|
|
@@ -9173,8 +9206,8 @@ function createTurnControlMcpServer(acts, skipState) {
|
|
|
9173
9206
|
WITHDRAW_ASK_TOOL,
|
|
9174
9207
|
TURN_CONTROL_TOOL_DESCRIPTIONS.withdraw_ask,
|
|
9175
9208
|
{
|
|
9176
|
-
askId:
|
|
9177
|
-
reason:
|
|
9209
|
+
askId: z18.string().uuid().describe(TURN_CONTROL_FIELD_DESCRIPTIONS.withdraw_ask.askId),
|
|
9210
|
+
reason: z18.string().min(1).max(MAX_WITHDRAW_REASON_BYTES).describe(TURN_CONTROL_FIELD_DESCRIPTIONS.withdraw_ask.reason)
|
|
9178
9211
|
},
|
|
9179
9212
|
async (args) => performAct(acts, {
|
|
9180
9213
|
kind: "withdraw_ask",
|
|
@@ -12848,7 +12881,7 @@ function unusableReason(runtime, check) {
|
|
|
12848
12881
|
if (check.status === "check_failed") return failureText(runtime, check.reason);
|
|
12849
12882
|
return null;
|
|
12850
12883
|
}
|
|
12851
|
-
var SETUP_AGAIN = `Then
|
|
12884
|
+
var SETUP_AGAIN = `Then ${cabane("status")} shows it signed in; nothing else to run.`;
|
|
12852
12885
|
function spaced(sentence) {
|
|
12853
12886
|
return sentence ? ` ${sentence}` : "";
|
|
12854
12887
|
}
|
|
@@ -13572,27 +13605,27 @@ function compareVersions(a, b) {
|
|
|
13572
13605
|
// src/update-state.ts
|
|
13573
13606
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync15, renameSync as renameSync10, writeFileSync as writeFileSync10 } from "fs";
|
|
13574
13607
|
import { dirname as dirname12, join as join28 } from "path";
|
|
13575
|
-
import { z as
|
|
13576
|
-
var resultSchema =
|
|
13577
|
-
kind:
|
|
13578
|
-
from:
|
|
13579
|
-
to:
|
|
13580
|
-
at:
|
|
13581
|
-
reason:
|
|
13608
|
+
import { z as z19 } from "zod";
|
|
13609
|
+
var resultSchema = z19.object({
|
|
13610
|
+
kind: z19.enum(["updated", "rolled_back", "failed"]),
|
|
13611
|
+
from: z19.string(),
|
|
13612
|
+
to: z19.string(),
|
|
13613
|
+
at: z19.string(),
|
|
13614
|
+
reason: z19.string().optional()
|
|
13582
13615
|
});
|
|
13583
|
-
var pendingSchema =
|
|
13584
|
-
from:
|
|
13616
|
+
var pendingSchema = z19.object({
|
|
13617
|
+
from: z19.string(),
|
|
13585
13618
|
// The package root `current` pointed at before the swap, so a rollback can point it back.
|
|
13586
|
-
fromRoot:
|
|
13587
|
-
to:
|
|
13588
|
-
at:
|
|
13589
|
-
boots:
|
|
13619
|
+
fromRoot: z19.string(),
|
|
13620
|
+
to: z19.string(),
|
|
13621
|
+
at: z19.string(),
|
|
13622
|
+
boots: z19.number().int().nonnegative()
|
|
13590
13623
|
});
|
|
13591
|
-
var stateSchema =
|
|
13592
|
-
server:
|
|
13624
|
+
var stateSchema = z19.object({
|
|
13625
|
+
server: z19.object({ latest: z19.string(), minimum: z19.string(), at: z19.string() }).optional(),
|
|
13593
13626
|
pending: pendingSchema.optional(),
|
|
13594
13627
|
lastResult: resultSchema.optional(),
|
|
13595
|
-
failed:
|
|
13628
|
+
failed: z19.object({ version: z19.string(), attempts: z19.number().int(), at: z19.string() }).optional()
|
|
13596
13629
|
});
|
|
13597
13630
|
function updateStatePath(dir2 = cabaneDir()) {
|
|
13598
13631
|
return join28(dir2, "update.json");
|
|
@@ -14080,6 +14113,13 @@ var CompanionSupervisor = class {
|
|
|
14080
14113
|
recoveryChecked = false;
|
|
14081
14114
|
recovering = false;
|
|
14082
14115
|
heartbeated = false;
|
|
14116
|
+
heartbeatStatus = emptyHeartbeatStatus();
|
|
14117
|
+
currentDetection() {
|
|
14118
|
+
return this.detection;
|
|
14119
|
+
}
|
|
14120
|
+
lastHeartbeat() {
|
|
14121
|
+
return { ...this.heartbeatStatus };
|
|
14122
|
+
}
|
|
14083
14123
|
recoveryCursor;
|
|
14084
14124
|
recoveredTurns = /* @__PURE__ */ new Set();
|
|
14085
14125
|
// Agents assigned here, as `workspaceId|agentId`, whose sign-in to Cabane is missing and
|
|
@@ -14199,19 +14239,26 @@ var CompanionSupervisor = class {
|
|
|
14199
14239
|
else this.log.error({ posture }, `runners: ${summary}`);
|
|
14200
14240
|
void this.companionStores().refresh();
|
|
14201
14241
|
}
|
|
14202
|
-
|
|
14242
|
+
if (this.config.deviceToken) {
|
|
14243
|
+
this.deviceApi = new DeviceApi({
|
|
14244
|
+
baseUrl: this.config.baseUrl,
|
|
14245
|
+
deviceToken: this.config.deviceToken
|
|
14246
|
+
});
|
|
14247
|
+
this.trackHeartbeat(this.sendHeartbeat(true));
|
|
14248
|
+
}
|
|
14249
|
+
const firstHeartbeat = this.inFlightHeartbeat;
|
|
14250
|
+
const startupHarnessRefresh = this.detect();
|
|
14251
|
+
void startupHarnessRefresh.then(async () => {
|
|
14252
|
+
await firstHeartbeat;
|
|
14253
|
+
if (this.stopped) return;
|
|
14254
|
+
await this.heartbeatNow();
|
|
14203
14255
|
});
|
|
14204
|
-
this.trackHeartbeat(startupHarnessRefresh);
|
|
14205
14256
|
this.detectTimer = setInterval(() => void this.detect(), DETECT_INTERVAL_MS);
|
|
14206
14257
|
this.detectTimer.unref?.();
|
|
14207
|
-
if (!this.config.deviceToken) {
|
|
14208
|
-
this.log.warn("This device isn't paired.
|
|
14258
|
+
if (!this.deviceApi || !this.config.deviceToken) {
|
|
14259
|
+
this.log.warn("This device isn't paired. Get a setup line from Settings \u2192 Devices.");
|
|
14209
14260
|
return;
|
|
14210
14261
|
}
|
|
14211
|
-
this.deviceApi = new DeviceApi({
|
|
14212
|
-
baseUrl: this.config.baseUrl,
|
|
14213
|
-
deviceToken: this.config.deviceToken
|
|
14214
|
-
});
|
|
14215
14262
|
this.deviceSub = new SseSubscriber({
|
|
14216
14263
|
baseUrl: this.config.baseUrl,
|
|
14217
14264
|
workspaceId: "device-control",
|
|
@@ -14257,11 +14304,6 @@ var CompanionSupervisor = class {
|
|
|
14257
14304
|
this.updater.start();
|
|
14258
14305
|
await this.refreshAssignments();
|
|
14259
14306
|
this.deviceSub.start();
|
|
14260
|
-
const firstHeartbeat = startupHarnessRefresh.then(async () => {
|
|
14261
|
-
if (this.stopped) return;
|
|
14262
|
-
await this.sendHeartbeat();
|
|
14263
|
-
});
|
|
14264
|
-
this.trackHeartbeat(firstHeartbeat);
|
|
14265
14307
|
this.heartbeatTimer = setInterval(() => this.kickHeartbeat(), HEARTBEAT_INTERVAL_MS);
|
|
14266
14308
|
this.heartbeatTimer.unref?.();
|
|
14267
14309
|
this.pollTimer = setInterval(() => void this.refreshAssignments(), ASSIGNMENTS_POLL_MS);
|
|
@@ -14282,15 +14324,16 @@ var CompanionSupervisor = class {
|
|
|
14282
14324
|
if (this.inFlightHeartbeat === pending) this.inFlightHeartbeat = null;
|
|
14283
14325
|
});
|
|
14284
14326
|
}
|
|
14285
|
-
async sendHeartbeat() {
|
|
14327
|
+
async sendHeartbeat(first = false) {
|
|
14286
14328
|
if (!this.deviceApi) return;
|
|
14287
14329
|
try {
|
|
14288
|
-
if (!this.detection
|
|
14330
|
+
if (!first && this.detection && advertisedRuntimes(this.detection).length === 0)
|
|
14331
|
+
await this.detect();
|
|
14289
14332
|
const store = loadSecretStoreTolerant(
|
|
14290
14333
|
(m) => this.log.warn({ err: m }, "Could not read the secrets file. Check ~/.cabane/secrets.json.")
|
|
14291
14334
|
);
|
|
14292
14335
|
const connectorReports = this.connectorReports();
|
|
14293
|
-
const opencodeUrl = this.opencodeUrl();
|
|
14336
|
+
const opencodeUrl = first ? null : this.opencodeUrl();
|
|
14294
14337
|
const opencodeModels = opencodeUrl ? await enumerateOpencodeModels(opencodeUrl) : null;
|
|
14295
14338
|
const res = await this.deviceApi.heartbeat({
|
|
14296
14339
|
bootId,
|
|
@@ -14299,31 +14342,39 @@ var CompanionSupervisor = class {
|
|
|
14299
14342
|
// Each runtime only when this device can actually run it: what the last
|
|
14300
14343
|
// detection found usable (signed in, or an API key, or a serve
|
|
14301
14344
|
// answering) — a usable bundled binary is a present one.
|
|
14302
|
-
|
|
14303
|
-
|
|
14304
|
-
|
|
14305
|
-
|
|
14306
|
-
|
|
14307
|
-
|
|
14308
|
-
|
|
14309
|
-
|
|
14310
|
-
|
|
14311
|
-
|
|
14312
|
-
|
|
14313
|
-
|
|
14345
|
+
...!first && this.detection ? {
|
|
14346
|
+
manifest: buildCompanionManifest({
|
|
14347
|
+
claudeCode: this.offered("claude-code"),
|
|
14348
|
+
opencode: this.offered("opencode"),
|
|
14349
|
+
codex: this.offered("codex"),
|
|
14350
|
+
// WHY an allowed harness is absent above. Diagnostic only, and always
|
|
14351
|
+
// sent, so an empty array clears a previously reported issue.
|
|
14352
|
+
runtimeIssues: this.runtimeIssues(),
|
|
14353
|
+
// Informational only; the server matches on name.
|
|
14354
|
+
versions: this.harnessVersions,
|
|
14355
|
+
// Shown on the device page.
|
|
14356
|
+
...runnersEnabled(this.config) ? { runner: this.runnerManifest() } : {}
|
|
14357
|
+
})
|
|
14358
|
+
} : {},
|
|
14314
14359
|
// Omitted until the first observed failure or heal, so the server's
|
|
14315
14360
|
// status-less manifest synthesis still runs for a fresh companion.
|
|
14316
|
-
|
|
14361
|
+
...!first && connectorReports.length > 0 ? { connectors: connectorReports } : {},
|
|
14317
14362
|
// The complete harness inventory in the machine's words, once detection
|
|
14318
14363
|
// has run; omitted before it, which the server reads as "nothing yet".
|
|
14319
|
-
|
|
14364
|
+
...!first && this.detection ? { harnesses: harnessEntriesFromDetection(this.detection) } : {},
|
|
14320
14365
|
...opencodeModels !== null ? { models: opencodeModels } : {},
|
|
14321
|
-
|
|
14366
|
+
...!first && this.updater ? { update: this.updater.report() } : {}
|
|
14322
14367
|
});
|
|
14323
14368
|
this.hub.setDevice({ deviceId: res.deviceId });
|
|
14324
14369
|
this.deviceId = res.deviceId;
|
|
14325
14370
|
this.checkVersionSkew(res.serverVersion);
|
|
14326
14371
|
this.heartbeated = true;
|
|
14372
|
+
const view = acknowledgedDeviceSchema.safeParse(res.device);
|
|
14373
|
+
this.heartbeatStatus = {
|
|
14374
|
+
lastHeartbeatAckAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14375
|
+
lastAcknowledgedDevice: view.success ? view.data : null,
|
|
14376
|
+
lastHeartbeatRefused: false
|
|
14377
|
+
};
|
|
14327
14378
|
void this.recoverRuns();
|
|
14328
14379
|
void this.updater?.onHeartbeat(res.companion, this.deviceConnected).catch(
|
|
14329
14380
|
(err) => this.log.warn(
|
|
@@ -14332,9 +14383,10 @@ var CompanionSupervisor = class {
|
|
|
14332
14383
|
)
|
|
14333
14384
|
);
|
|
14334
14385
|
} catch (err) {
|
|
14386
|
+
this.heartbeatStatus.lastHeartbeatRefused = err instanceof ApiError && (err.status === 401 || err.status === 403);
|
|
14335
14387
|
this.log.warn(
|
|
14336
14388
|
{ ...apiErrorLogFields(err) },
|
|
14337
|
-
"Couldn't check in with
|
|
14389
|
+
"Couldn't check in with the server; trying again shortly."
|
|
14338
14390
|
);
|
|
14339
14391
|
}
|
|
14340
14392
|
}
|
|
@@ -15421,7 +15473,8 @@ var CompanionSupervisor = class {
|
|
|
15421
15473
|
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
15422
15474
|
if (this.detectTimer) clearInterval(this.detectTimer);
|
|
15423
15475
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
15424
|
-
const heartbeat = this.inFlightHeartbeat
|
|
15476
|
+
const heartbeat = Promise.all([this.inFlightHeartbeat, this.detecting]).then(() => {
|
|
15477
|
+
});
|
|
15425
15478
|
this.deviceSub?.stop();
|
|
15426
15479
|
for (const wr of this.workspaces.values()) {
|
|
15427
15480
|
for (const a of wr.agents.values()) a.cancelDrain();
|
|
@@ -15699,7 +15752,11 @@ async function createCompanionRuntime(opts = {}) {
|
|
|
15699
15752
|
let dashboard = null;
|
|
15700
15753
|
try {
|
|
15701
15754
|
control = await startControlServer({
|
|
15702
|
-
status: () =>
|
|
15755
|
+
status: () => ({
|
|
15756
|
+
...hub.statusJson(),
|
|
15757
|
+
...supervisor.lastHeartbeat(),
|
|
15758
|
+
detection: supervisor.currentDetection()
|
|
15759
|
+
}),
|
|
15703
15760
|
detect: () => supervisor.redetect(),
|
|
15704
15761
|
allow: (runtime) => supervisor.allowHarness(runtime),
|
|
15705
15762
|
update: (auto) => supervisor.update(auto),
|