@tangle-network/agent-app 0.60.172 → 0.60.173
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/assistant/index.js +4 -4
- package/dist/chat-react/index.js +3 -3
- package/dist/chat-routes/index.js +2 -2
- package/dist/{chunk-KICTB6RK.js → chunk-F2BXNZAH.js} +2 -2
- package/dist/{chunk-CRHXK3QT.js → chunk-GYQTJA6N.js} +3 -3
- package/dist/{chunk-PZHZV6QY.js → chunk-PP5KIVHL.js} +2 -2
- package/dist/{chunk-RCRC2JKP.js → chunk-QW4UOEPH.js} +2 -2
- package/dist/{chunk-MXNVSY6J.js → chunk-VBRUG4N5.js} +47 -12
- package/dist/chunk-VBRUG4N5.js.map +1 -0
- package/dist/harness/index.d.ts +33 -74
- package/dist/harness/index.js +3 -1
- package/dist/harness/session-selection.d.ts +35 -0
- package/dist/sandbox/index.js +2 -2
- package/dist/skills-placement/index.js +1 -1
- package/dist/web-react/agent-profile-editor.js +3 -3
- package/dist/web-react/index.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-MXNVSY6J.js.map +0 -1
- /package/dist/{chunk-KICTB6RK.js.map → chunk-F2BXNZAH.js.map} +0 -0
- /package/dist/{chunk-CRHXK3QT.js.map → chunk-GYQTJA6N.js.map} +0 -0
- /package/dist/{chunk-PZHZV6QY.js.map → chunk-PP5KIVHL.js.map} +0 -0
- /package/dist/{chunk-RCRC2JKP.js.map → chunk-QW4UOEPH.js.map} +0 -0
package/dist/assistant/index.js
CHANGED
|
@@ -8,15 +8,15 @@ import "../chunk-FBVLEGEG.js";
|
|
|
8
8
|
import "../chunk-CW6F6GDP.js";
|
|
9
9
|
import "../chunk-ILZQAXJH.js";
|
|
10
10
|
import "../chunk-E4AY62T2.js";
|
|
11
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-GYQTJA6N.js";
|
|
12
12
|
import "../chunk-Y6PRDUFJ.js";
|
|
13
13
|
import "../chunk-GEYACSFW.js";
|
|
14
14
|
import {
|
|
15
15
|
ChatComposer
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-PP5KIVHL.js";
|
|
17
17
|
import "../chunk-L7MB2LLM.js";
|
|
18
18
|
import "../chunk-SJWIZT7B.js";
|
|
19
|
-
import "../chunk-
|
|
19
|
+
import "../chunk-F2BXNZAH.js";
|
|
20
20
|
import "../chunk-R5K5ML3Q.js";
|
|
21
21
|
import {
|
|
22
22
|
ModelPicker
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
} from "../chunk-LSUELMHY.js";
|
|
28
28
|
import "../chunk-FQYAJYPW.js";
|
|
29
29
|
import "../chunk-WTC6AOPL.js";
|
|
30
|
-
import "../chunk-
|
|
30
|
+
import "../chunk-VBRUG4N5.js";
|
|
31
31
|
import "../chunk-B7CL7ENS.js";
|
|
32
32
|
import "../chunk-PV33JZKV.js";
|
|
33
33
|
import "../chunk-AMUXRAUO.js";
|
package/dist/chat-react/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComposerModeControls,
|
|
3
3
|
EntryComposer
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-PP5KIVHL.js";
|
|
5
5
|
import "../chunk-L7MB2LLM.js";
|
|
6
6
|
import "../chunk-SJWIZT7B.js";
|
|
7
|
-
import "../chunk-
|
|
7
|
+
import "../chunk-F2BXNZAH.js";
|
|
8
8
|
import "../chunk-R5K5ML3Q.js";
|
|
9
9
|
import "../chunk-AA4FKEQW.js";
|
|
10
10
|
import "../chunk-RASGKEAL.js";
|
|
11
11
|
import "../chunk-WTC6AOPL.js";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-VBRUG4N5.js";
|
|
13
13
|
import "../chunk-AMUXRAUO.js";
|
|
14
14
|
import "../chunk-YHRWZZJN.js";
|
|
15
15
|
import "../chunk-TXD5HXLE.js";
|
|
@@ -42,9 +42,9 @@ import {
|
|
|
42
42
|
flattenHistory,
|
|
43
43
|
readSandboxBinaryBytes,
|
|
44
44
|
statSandboxFileSize
|
|
45
|
-
} from "../chunk-
|
|
45
|
+
} from "../chunk-QW4UOEPH.js";
|
|
46
46
|
import "../chunk-4LBCTFBP.js";
|
|
47
|
-
import "../chunk-
|
|
47
|
+
import "../chunk-VBRUG4N5.js";
|
|
48
48
|
import "../chunk-LWSJK546.js";
|
|
49
49
|
import "../chunk-74WXGIEK.js";
|
|
50
50
|
import "../chunk-6A7MYOUI.js";
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import {
|
|
16
16
|
snapHarnessToModel,
|
|
17
17
|
snapModelToHarness
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-VBRUG4N5.js";
|
|
19
19
|
|
|
20
20
|
// src/web-react/composer-file-accept.ts
|
|
21
21
|
function isAcceptedFileType(file, accept) {
|
|
@@ -576,4 +576,4 @@ export {
|
|
|
576
576
|
HarnessPicker,
|
|
577
577
|
AgentSessionControls
|
|
578
578
|
};
|
|
579
|
-
//# sourceMappingURL=chunk-
|
|
579
|
+
//# sourceMappingURL=chunk-F2BXNZAH.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HarnessPicker,
|
|
3
3
|
filterAcceptedFiles
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-F2BXNZAH.js";
|
|
5
5
|
import {
|
|
6
6
|
EffortPicker,
|
|
7
7
|
ModelPicker,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
isModelCompatibleWithHarness,
|
|
15
15
|
snapModelToHarness
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VBRUG4N5.js";
|
|
17
17
|
import {
|
|
18
18
|
isCommitSha,
|
|
19
19
|
parseGitHubLocation,
|
|
@@ -2214,4 +2214,4 @@ export {
|
|
|
2214
2214
|
AGENT_PROFILE_EDITOR_SECTIONS,
|
|
2215
2215
|
AgentProfileEditor
|
|
2216
2216
|
};
|
|
2217
|
-
//# sourceMappingURL=chunk-
|
|
2217
|
+
//# sourceMappingURL=chunk-GYQTJA6N.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
filterAcceptedFiles,
|
|
11
11
|
isAcceptedFileType,
|
|
12
12
|
renamePastedImages
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-F2BXNZAH.js";
|
|
14
14
|
import {
|
|
15
15
|
OVERLAY_SHADOW,
|
|
16
16
|
POPOVER_OPTION_FOCUS,
|
|
@@ -1552,4 +1552,4 @@ export {
|
|
|
1552
1552
|
ComposerModeControls,
|
|
1553
1553
|
EntryComposer
|
|
1554
1554
|
};
|
|
1555
|
-
//# sourceMappingURL=chunk-
|
|
1555
|
+
//# sourceMappingURL=chunk-PP5KIVHL.js.map
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
DEFAULT_HARNESS,
|
|
14
14
|
assertHarnessModelCompatible,
|
|
15
15
|
isHarness
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VBRUG4N5.js";
|
|
17
17
|
import {
|
|
18
18
|
assertProfilePromptWithinBudget,
|
|
19
19
|
fingerprintAgentProfile
|
|
@@ -3343,4 +3343,4 @@ export {
|
|
|
3343
3343
|
isTerminalPromptEvent,
|
|
3344
3344
|
detectInteractiveQuestion
|
|
3345
3345
|
};
|
|
3346
|
-
//# sourceMappingURL=chunk-
|
|
3346
|
+
//# sourceMappingURL=chunk-QW4UOEPH.js.map
|
|
@@ -8,6 +8,50 @@ import {
|
|
|
8
8
|
snapHarnessToModel as aiSnapHarnessToModel,
|
|
9
9
|
snapModelToHarness as aiSnapModelToHarness
|
|
10
10
|
} from "@tangle-network/agent-interface";
|
|
11
|
+
|
|
12
|
+
// src/harness/session-selection.ts
|
|
13
|
+
var HarnessSelectionError = class extends Error {
|
|
14
|
+
code;
|
|
15
|
+
harness;
|
|
16
|
+
source;
|
|
17
|
+
constructor(code, value, source) {
|
|
18
|
+
const harness = typeof value === "string" ? value : null;
|
|
19
|
+
super(`${code}: ${source} harness ${harness === null ? "<invalid>" : JSON.stringify(harness)}`);
|
|
20
|
+
this.name = "HarnessSelectionError";
|
|
21
|
+
this.code = code;
|
|
22
|
+
this.harness = harness;
|
|
23
|
+
this.source = source;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function selectSessionHarness(input, knownHarnesses, defaultHarness) {
|
|
27
|
+
const known = new Set(knownHarnesses);
|
|
28
|
+
const resolve = (value2, source2) => {
|
|
29
|
+
if (typeof value2 !== "string" || !known.has(value2)) {
|
|
30
|
+
throw new HarnessSelectionError("harness_unknown", value2, source2);
|
|
31
|
+
}
|
|
32
|
+
return value2;
|
|
33
|
+
};
|
|
34
|
+
const admit = (harness2, source2, existing) => {
|
|
35
|
+
if (input.availability === void 0) return;
|
|
36
|
+
const allowed = existing ? input.availability.existingSessions ?? input.availability.newSessions : input.availability.newSessions;
|
|
37
|
+
if (!allowed.includes(harness2)) {
|
|
38
|
+
throw new HarnessSelectionError("harness_unavailable", harness2, source2);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
if (input.sessionHarness !== void 0 && input.sessionHarness !== null) {
|
|
42
|
+
const harness2 = resolve(input.sessionHarness, "session");
|
|
43
|
+
admit(harness2, "session", true);
|
|
44
|
+
const requested = input.requested === void 0 || input.requested === null ? void 0 : resolve(input.requested, "requested");
|
|
45
|
+
return { harness: harness2, locked: true, swapAttempted: requested !== void 0 && requested !== harness2 };
|
|
46
|
+
}
|
|
47
|
+
const source = input.requested !== void 0 && input.requested !== null ? "requested" : input.workspaceDefault !== void 0 && input.workspaceDefault !== null ? "workspace" : "fallback";
|
|
48
|
+
const value = source === "requested" ? input.requested : source === "workspace" ? input.workspaceDefault : input.fallback ?? defaultHarness;
|
|
49
|
+
const harness = resolve(value, source);
|
|
50
|
+
admit(harness, source, false);
|
|
51
|
+
return { harness, locked: false, swapAttempted: false };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/harness/index.ts
|
|
11
55
|
var NON_BACKEND_HARNESSES = ["gemini"];
|
|
12
56
|
var nonBackendHarnesses = new Set(NON_BACKEND_HARNESSES);
|
|
13
57
|
var KNOWN_HARNESSES = [
|
|
@@ -24,14 +68,7 @@ function coerceHarness(value, fallback = DEFAULT_HARNESS) {
|
|
|
24
68
|
return isHarness(value) ? value : fallback;
|
|
25
69
|
}
|
|
26
70
|
function resolveSessionHarness(input = {}) {
|
|
27
|
-
|
|
28
|
-
if (isHarness(input.sessionHarness)) {
|
|
29
|
-
const locked = input.sessionHarness;
|
|
30
|
-
const swapAttempted = isHarness(input.requested) && input.requested !== locked;
|
|
31
|
-
return { harness: locked, locked: true, swapAttempted };
|
|
32
|
-
}
|
|
33
|
-
const harness = coerceHarness(input.requested, coerceHarness(input.workspaceDefault, fallback));
|
|
34
|
-
return { harness, locked: false, swapAttempted: false };
|
|
71
|
+
return selectSessionHarness(input, KNOWN_HARNESSES, DEFAULT_HARNESS);
|
|
35
72
|
}
|
|
36
73
|
function configHarness(value, where) {
|
|
37
74
|
if (!isHarness(value)) throw new Error(`${where} names an unknown harness: ${String(value)}`);
|
|
@@ -93,9 +130,6 @@ var HARNESS_CAPABILITIES = {
|
|
|
93
130
|
prime: { subagents: true, mcp: "native", toolControls: "runtime", skillsDir: ".prime/agent/skills" },
|
|
94
131
|
hermes: { subagents: false, mcp: "native", toolControls: "runtime", skillsDir: null },
|
|
95
132
|
openclaw: { subagents: false, mcp: "config-file", toolControls: "runtime", skillsDir: "skills" },
|
|
96
|
-
// Harnesses the platform placement map does not bridge yet: conservative
|
|
97
|
-
// facts — assert nothing they have not proven. Each upgrades to its real
|
|
98
|
-
// facts when the materializer grows support for it.
|
|
99
133
|
acp: { subagents: false, mcp: "native", toolControls: "runtime", skillsDir: null },
|
|
100
134
|
amp: { subagents: false, mcp: "native", toolControls: "runtime", skillsDir: null },
|
|
101
135
|
"factory-droids": { subagents: false, mcp: "native", toolControls: "runtime", skillsDir: null },
|
|
@@ -110,6 +144,7 @@ function harnessCapabilities(harness) {
|
|
|
110
144
|
}
|
|
111
145
|
|
|
112
146
|
export {
|
|
147
|
+
HarnessSelectionError,
|
|
113
148
|
modelProvider,
|
|
114
149
|
KNOWN_HARNESSES,
|
|
115
150
|
DEFAULT_HARNESS,
|
|
@@ -123,4 +158,4 @@ export {
|
|
|
123
158
|
assertHarnessModelCompatible,
|
|
124
159
|
harnessCapabilities
|
|
125
160
|
};
|
|
126
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-VBRUG4N5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/harness/index.ts","../src/harness/session-selection.ts"],"sourcesContent":["/**\n * Coding-agent harness selection, model compatibility, and the session lock.\n *\n * Known identities are not a deployment's available runners. Products supply\n * server-owned availability to resolveSessionHarness. Historical session IDs\n * remain intact when their runner is unavailable; a harness change requires an\n * explicit transfer, not a fallback or a rewritten session lock.\n *\n * Model compatibility is owned by @tangle-network/agent-interface. The shell\n * owns session selection; the consumer owns storage and deployment policy.\n */\n\nimport {\n harnessProviders,\n harnessSupportsModel,\n harnessTypeSchema,\n modelProvider,\n preferredHarnessForModel,\n snapHarnessToModel as aiSnapHarnessToModel,\n snapModelToHarness as aiSnapModelToHarness,\n type HarnessType,\n} from '@tangle-network/agent-interface'\nimport {\n selectSessionHarness,\n type HarnessAvailability as SelectionAvailability,\n type SessionHarnessSelection,\n type SessionHarnessSelectionInput,\n} from './session-selection'\n\nexport { HarnessSelectionError } from './session-selection'\n\n/** Canonical identities that have no direct Sandbox backend. */\nconst NON_BACKEND_HARNESSES = ['gemini'] as const satisfies readonly HarnessType[]\nconst nonBackendHarnesses = new Set<string>(NON_BACKEND_HARNESSES)\n\n/** A recognized coding-agent backend, not a promise that it is installed. */\nexport type Harness = Exclude<HarnessType, (typeof NON_BACKEND_HARNESSES)[number]>\n\n/**\n * Recognized backend identities, including IDs needed by historical sessions.\n * Use deployment availability, not membership here, to offer or admit new runs.\n */\nexport const KNOWN_HARNESSES: readonly Harness[] = [\n ...harnessTypeSchema.options.filter(\n (harness): harness is Exclude<HarnessType, (typeof NON_BACKEND_HARNESSES)[number]> =>\n !nonBackendHarnesses.has(harness),\n ),\n]\n\n/** Default for an absent choice; never a replacement for an invalid explicit ID. */\nexport const DEFAULT_HARNESS: Harness = 'opencode'\nconst HARNESS_SET: ReadonlySet<string> = new Set(KNOWN_HARNESSES)\n\n/** Determine whether a value is a recognized identity, independently of availability. */\nexport function isHarness(value: unknown): value is Harness {\n return typeof value === 'string' && HARNESS_SET.has(value)\n}\n\n/** Display/config convenience only. Never use coercion to admit an execution or restore a lock. */\nexport function coerceHarness(value: unknown, fallback: Harness = DEFAULT_HARNESS): Harness {\n return isHarness(value) ? value : fallback\n}\n\n/** Server-owned start and resume sets. Existing-only entries are draining. */\nexport type HarnessAvailability = SelectionAvailability<Harness>\n\n/** A saved lock, explicit choice, workspace default, and optional deployment availability. */\nexport type ResolveSessionHarnessInput = SessionHarnessSelectionInput<Harness>\n\n/** The selected identity and the existing session-lock/swap-attempt result. */\nexport type ResolvedSessionHarness = SessionHarnessSelection<Harness>\n\n/**\n * Resolve requested → workspaceDefault → fallback only for a genuinely new session.\n * A non-null saved identity always takes the lock path. Unknown/retired identities\n * and invalid explicit requests throw HarnessSelectionError instead of falling back.\n * A known different request still reports swapAttempted while preserving the lock.\n * The caller must reject that attempt before execution, as in the existing contract.\n */\nexport function resolveSessionHarness(input: ResolveSessionHarnessInput = {}): ResolvedSessionHarness {\n return selectSessionHarness(input, KNOWN_HARNESSES, DEFAULT_HARNESS)\n}\n\n/**\n * An app's harness configuration, read from its deploy configuration (for example the env vars\n * `AGENT_HARNESS` and `AGENT_HARNESS_WORKSPACES`), so no product hard-codes `backend.type`.\n */\nexport interface HarnessConfig {\n /** The app default for new sessions. Unset or blank means {@link DEFAULT_HARNESS}. */\n default?: string\n /** Per-workspace choices: a JSON object mapping a workspace id to a harness. */\n workspaces?: string\n}\n\nfunction configHarness(value: unknown, where: string): Harness {\n if (!isHarness(value)) throw new Error(`${where} names an unknown harness: ${String(value)}`)\n return value\n}\n\n/**\n * The harness a workspace's new sessions start on: its own choice, else the app default, else\n * {@link DEFAULT_HARNESS}. Pass the result to {@link resolveSessionHarness} as `workspaceDefault`\n * so a session that already ran keeps its harness. A value naming an unknown harness throws:\n * a configuration typo must fail loudly, never run a different harness than the one configured.\n */\nexport function configuredHarness(config: HarnessConfig, workspaceId?: string): Harness {\n const appDefault = config.default?.trim()\n ? configHarness(config.default.trim(), 'The harness default') : DEFAULT_HARNESS\n const raw = config.workspaces?.trim()\n if (!raw) return appDefault\n let choices: unknown\n try {\n choices = JSON.parse(raw)\n } catch {\n throw new Error('The per-workspace harness configuration is not JSON')\n }\n if (!choices || typeof choices !== 'object' || Array.isArray(choices)) {\n throw new Error('The per-workspace harness configuration must map workspace ids to harnesses')\n }\n // Every entry is checked, not only the caller's, so one bad entry fails every workspace's\n // first turn after the deploy instead of hiding until that workspace sends a message.\n const parsed = new Map(Object.entries(choices).map(([id, value]) =>\n [id, configHarness(value, `The harness for workspace ${id}`)]))\n return (workspaceId === undefined ? undefined : parsed.get(workspaceId)) ?? appDefault\n}\n\n/** Harness-model compatibility is delegated to the Interface owner. */\nexport { modelProvider }\n\n/** A resolved model and the transport provider that will execute it. */\nexport interface HarnessModelSelection {\n model: string\n provider?: string\n}\n\n/** Provider-less ids (sentinels like \"default\", or a session's own config) are\n * compatible everywhere — every harness honors its own configuration. */\nexport function isModelCompatibleWithHarness(harness: Harness, modelId: string): boolean {\n return harnessSupportsModel(harness, modelId)\n}\n\n/** Keep `modelId` when the harness can run it; else the harness's best compatible\n * catalog id (preferred patterns in order, highest version). When nothing in the\n * catalog fits, return the original so the caller sees the incompatibility. */\nexport function snapModelToHarness(harness: Harness, modelId: string, canonicalIds: readonly string[]): string {\n return aiSnapModelToHarness(harness, modelId, canonicalIds)\n}\n\n/** Keep the harness when it can run `modelId`; else the model's native harness\n * (anthropic → claude-code, openai → codex, moonshot → kimi-code), falling back to opencode. */\nexport function snapHarnessToModel(harness: Harness, modelId: string): Harness {\n const snapped = aiSnapHarnessToModel(harness, modelId)\n if (!isHarness(snapped)) {\n throw new Error(\n `Harness \"${harness}\" snapped to \"${snapped}\" for model \"${modelId}\", which this platform has no backend for.`,\n )\n }\n return snapped\n}\n\n/** Fail-loud server guard: throw when a harness is asked to run a model it can't.\n * A resolved model tuple supplies provider evidence when its id is unqualified.\n * Call before dispatching a sandbox turn so a bypassed UI cannot reach the\n * sidecar with an incompatible or unproven pair. */\nexport function assertHarnessModelCompatible(\n harness: Harness,\n selection: string | HarnessModelSelection,\n): void {\n const modelId = typeof selection === 'string' ? selection : selection.model\n const transportProvider = typeof selection === 'string' ? undefined : selection.provider\n const providers = harnessProviders(harness)\n const provider = modelProvider(modelId) ?? transportProvider ?? null\n // An omitted/default model is deliberately left to the harness. Once a\n // caller names a model, however, a vendor-locked harness must have provider\n // evidence. Treating a router provider as compatible with every native CLI\n // is how a bare Gemini model reached Claude Code and produced \"Not logged in\".\n const isDefaultSentinel = modelId.trim() === '' || modelId.trim() === 'default'\n const compatible = isDefaultSentinel\n || providers === null\n || (provider !== null && providers.includes(provider))\n if (!compatible) {\n const native = preferredHarnessForModel(modelId)\n const providerDescription = provider ?? 'unqualified'\n throw new Error(\n `Harness \"${harness}\" cannot run model \"${modelId}\" (provider \"${providerDescription}\"). ` +\n `Use ${native ?? 'a router-backed harness (opencode)'} or an allowed model.`,\n )\n }\n}\n\n// Profile-support facts remain here until the materializer supplies its owned descriptor.\n// Do not confuse these facts with deployment availability.\n\n/** What a harness can express, for profile assembly. */\nexport interface HarnessCapabilities {\n /** Lower profile.subagents into native cwd files; false means materialization refuses them. */\n subagents: boolean\n /** How profile.mcp reaches the harness. */\n mcp: 'native' | 'config-file' | 'extension' | 'none'\n /** Whether tool controls are checked before MCP tools register. */\n toolControls: 'startup' | 'runtime'\n /** cwd-relative skill directory, or null when skills must be inlined. */\n skillsDir: string | null\n}\n\nconst HARNESS_CAPABILITIES: Record<Harness, HarnessCapabilities> = {\n 'claude-code': { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.claude/skills' },\n nanoclaw: { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.claude/skills' },\n opencode: { subagents: true, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.opencode/skills' },\n codex: { subagents: true, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.codex/skills' },\n 'kimi-code': { subagents: false, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.kimi/skills' },\n pi: { subagents: false, mcp: 'extension', toolControls: 'startup', skillsDir: '.pi/skills' },\n prime: { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.prime/agent/skills' },\n hermes: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n openclaw: { subagents: false, mcp: 'config-file', toolControls: 'runtime', skillsDir: 'skills' },\n acp: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n amp: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n 'factory-droids': { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n forge: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n cursor: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n 'cli-base': { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n}\n\n/** Profile-support facts, independent of whether this deployment enables the harness. */\nexport function harnessCapabilities(harness: Harness): HarnessCapabilities {\n const caps = HARNESS_CAPABILITIES[harness]\n if (!caps) throw new Error(`harnessCapabilities: no capability facts for \"${harness}\"`)\n return caps\n}\n","/** Pure session admission. Harness identity is not deployment availability. */\nexport interface HarnessAvailability<H extends string> {\n /** Server-owned selection for new sessions. An empty list disables all starts. */\n readonly newSessions: readonly H[]\n /** Includes draining harnesses. Omission uses newSessions, never all known IDs. */\n readonly existingSessions?: readonly H[]\n}\n\nexport interface SessionHarnessSelectionInput<H extends string> {\n sessionHarness?: unknown\n requested?: unknown\n workspaceDefault?: unknown\n fallback?: H\n /** Omit only when the caller deliberately admits every known harness. */\n availability?: HarnessAvailability<H>\n}\n\nexport interface SessionHarnessSelection<H extends string> {\n harness: H\n locked: boolean\n swapAttempted: boolean\n}\n\ntype SelectionSource = 'session' | 'requested' | 'workspace' | 'fallback'\n\n/** An admission failure, never permission to retry on another harness. */\nexport class HarnessSelectionError extends Error {\n readonly code: 'harness_unknown' | 'harness_unavailable'\n readonly harness: string | null\n readonly source: SelectionSource\n\n constructor(\n code: 'harness_unknown' | 'harness_unavailable',\n value: unknown,\n source: SelectionSource,\n ) {\n const harness = typeof value === 'string' ? value : null\n super(`${code}: ${source} harness ${harness === null ? '<invalid>' : JSON.stringify(harness)}`)\n this.name = 'HarnessSelectionError'\n this.code = code\n this.harness = harness\n this.source = source\n }\n}\n\n/**\n * Null/undefined mean no choice. Every other value is an explicit choice and must\n * validate. A retired or unrecognized saved identity must never become a new session.\n * Availability is supplied by the authenticated server, not by a request body.\n */\nexport function selectSessionHarness<H extends string>(\n input: SessionHarnessSelectionInput<H>,\n knownHarnesses: readonly H[],\n defaultHarness: H,\n): SessionHarnessSelection<H> {\n const known = new Set<string>(knownHarnesses)\n const resolve = (value: unknown, source: SelectionSource): H => {\n if (typeof value !== 'string' || !known.has(value)) {\n throw new HarnessSelectionError('harness_unknown', value, source)\n }\n return value as H\n }\n const admit = (harness: H, source: SelectionSource, existing: boolean): void => {\n if (input.availability === undefined) return\n const allowed = existing\n ? input.availability.existingSessions ?? input.availability.newSessions\n : input.availability.newSessions\n if (!allowed.includes(harness)) {\n throw new HarnessSelectionError('harness_unavailable', harness, source)\n }\n }\n\n if (input.sessionHarness !== undefined && input.sessionHarness !== null) {\n const harness = resolve(input.sessionHarness, 'session')\n admit(harness, 'session', true)\n // A known different request remains a swap attempt; it is not executed. An\n // invalid request is refused rather than being misreported as no swap.\n const requested = input.requested === undefined || input.requested === null\n ? undefined : resolve(input.requested, 'requested')\n return { harness, locked: true, swapAttempted: requested !== undefined && requested !== harness }\n }\n\n const source: SelectionSource = input.requested !== undefined && input.requested !== null\n ? 'requested'\n : input.workspaceDefault !== undefined && input.workspaceDefault !== null\n ? 'workspace' : 'fallback'\n const value = source === 'requested' ? input.requested\n : source === 'workspace' ? input.workspaceDefault : input.fallback ?? defaultHarness\n const harness = resolve(value, source)\n admit(harness, source, false)\n return { harness, locked: false, swapAttempted: false }\n}\n"],"mappings":"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","names":["value","source","harness"]}
|
package/dist/harness/index.d.ts
CHANGED
|
@@ -1,73 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Coding-agent harness selection
|
|
2
|
+
* Coding-agent harness selection, model compatibility, and the session lock.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* orphan the session's running agent state). Every product otherwise hand-rolls
|
|
9
|
-
* this and hard-codes a single harness; this is the one place the rule lives.
|
|
4
|
+
* Known identities are not a deployment's available runners. Products supply
|
|
5
|
+
* server-owned availability to resolveSessionHarness. Historical session IDs
|
|
6
|
+
* remain intact when their runner is unavailable; a harness change requires an
|
|
7
|
+
* explicit transfer, not a fallback or a rewritten session lock.
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* owns storage — which harness a workspace defaults to, which one a session locked — and maps the
|
|
14
|
-
* resolved value onto the SDK's `backend.type`.
|
|
15
|
-
*
|
|
16
|
-
* Harness↔model COMPATIBILITY (which models a harness can run, snapping) is NOT defined here — it
|
|
17
|
-
* comes from `@tangle-network/agent-interface`, the single source of truth shared with the
|
|
18
|
-
* sandbox-ui pickers and the cli-bridge backends. This module owns the harness TAXONOMY + the
|
|
19
|
-
* session lock, and its server assertion also checks resolved transport-provider evidence.
|
|
9
|
+
* Model compatibility is owned by @tangle-network/agent-interface. The shell
|
|
10
|
+
* owns session selection; the consumer owns storage and deployment policy.
|
|
20
11
|
*/
|
|
21
12
|
import { modelProvider, type HarnessType } from '@tangle-network/agent-interface';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* offering it would resolve a session onto a runner that cannot start.
|
|
26
|
-
*/
|
|
13
|
+
import { type HarnessAvailability as SelectionAvailability, type SessionHarnessSelection, type SessionHarnessSelectionInput } from './session-selection';
|
|
14
|
+
export { HarnessSelectionError } from './session-selection';
|
|
15
|
+
/** Canonical identities that have no direct Sandbox backend. */
|
|
27
16
|
declare const NON_BACKEND_HARNESSES: readonly ["gemini"];
|
|
28
|
-
/** A coding-agent backend
|
|
17
|
+
/** A recognized coding-agent backend, not a promise that it is installed. */
|
|
29
18
|
export type Harness = Exclude<HarnessType, (typeof NON_BACKEND_HARNESSES)[number]>;
|
|
30
19
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* an edit here, and kept structural so this module still needs no sandbox dependency.
|
|
20
|
+
* Recognized backend identities, including IDs needed by historical sessions.
|
|
21
|
+
* Use deployment availability, not membership here, to offer or admit new runs.
|
|
34
22
|
*/
|
|
35
23
|
export declare const KNOWN_HARNESSES: readonly Harness[];
|
|
36
|
-
/**
|
|
24
|
+
/** Default for an absent choice; never a replacement for an invalid explicit ID. */
|
|
37
25
|
export declare const DEFAULT_HARNESS: Harness;
|
|
38
|
-
/** Determine
|
|
26
|
+
/** Determine whether a value is a recognized identity, independently of availability. */
|
|
39
27
|
export declare function isHarness(value: unknown): value is Harness;
|
|
40
|
-
/**
|
|
28
|
+
/** Display/config convenience only. Never use coercion to admit an execution or restore a lock. */
|
|
41
29
|
export declare function coerceHarness(value: unknown, fallback?: Harness): Harness;
|
|
42
|
-
/**
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
/** The workspace's default harness, used only when starting a fresh session. */
|
|
49
|
-
workspaceDefault?: unknown;
|
|
50
|
-
/** Final fallback when nothing else resolves (default `opencode`). */
|
|
51
|
-
fallback?: Harness;
|
|
52
|
-
}
|
|
53
|
-
/** Represent resolved session state including harness, lock status, and swap attempt flag */
|
|
54
|
-
export interface ResolvedSessionHarness {
|
|
55
|
-
/** The harness to actually run — the locked one when the session already has it. */
|
|
56
|
-
harness: Harness;
|
|
57
|
-
/** True when the session already had a locked harness (this turn did not pick it). */
|
|
58
|
-
locked: boolean;
|
|
59
|
-
/** True when `requested` differs from the locked harness — a forbidden mid-session
|
|
60
|
-
* swap the caller should reject or warn on. The lock always wins regardless. */
|
|
61
|
-
swapAttempted: boolean;
|
|
62
|
-
}
|
|
30
|
+
/** Server-owned start and resume sets. Existing-only entries are draining. */
|
|
31
|
+
export type HarnessAvailability = SelectionAvailability<Harness>;
|
|
32
|
+
/** A saved lock, explicit choice, workspace default, and optional deployment availability. */
|
|
33
|
+
export type ResolveSessionHarnessInput = SessionHarnessSelectionInput<Harness>;
|
|
34
|
+
/** The selected identity and the existing session-lock/swap-attempt result. */
|
|
35
|
+
export type ResolvedSessionHarness = SessionHarnessSelection<Harness>;
|
|
63
36
|
/**
|
|
64
|
-
* Resolve
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* - **Fresh session**: pick `requested → workspaceDefault → fallback`. The caller
|
|
70
|
-
* persists the result as the session's lock for every subsequent turn.
|
|
37
|
+
* Resolve requested → workspaceDefault → fallback only for a genuinely new session.
|
|
38
|
+
* A non-null saved identity always takes the lock path. Unknown/retired identities
|
|
39
|
+
* and invalid explicit requests throw HarnessSelectionError instead of falling back.
|
|
40
|
+
* A known different request still reports swapAttempted while preserving the lock.
|
|
41
|
+
* The caller must reject that attempt before execution, as in the existing contract.
|
|
71
42
|
*/
|
|
72
43
|
export declare function resolveSessionHarness(input?: ResolveSessionHarnessInput): ResolvedSessionHarness;
|
|
73
44
|
/**
|
|
@@ -87,13 +58,7 @@ export interface HarnessConfig {
|
|
|
87
58
|
* a configuration typo must fail loudly, never run a different harness than the one configured.
|
|
88
59
|
*/
|
|
89
60
|
export declare function configuredHarness(config: HarnessConfig, workspaceId?: string): Harness;
|
|
90
|
-
/**
|
|
91
|
-
* Harness ↔ model compatibility + snapping — delegated to `@tangle-network/agent-interface`.
|
|
92
|
-
*
|
|
93
|
-
* `Harness` is the dispatchable subset of `HarnessType`: this shell drops only canonical harnesses
|
|
94
|
-
* for which the platform has no backend. Compatibility therefore comes entirely from Interface;
|
|
95
|
-
* this package keeps no second harness taxonomy.
|
|
96
|
-
*/
|
|
61
|
+
/** Harness-model compatibility is delegated to the Interface owner. */
|
|
97
62
|
export { modelProvider };
|
|
98
63
|
/** A resolved model and the transport provider that will execute it. */
|
|
99
64
|
export interface HarnessModelSelection {
|
|
@@ -117,20 +82,14 @@ export declare function snapHarnessToModel(harness: Harness, modelId: string): H
|
|
|
117
82
|
export declare function assertHarnessModelCompatible(harness: Harness, selection: string | HarnessModelSelection): void;
|
|
118
83
|
/** What a harness can express, for profile assembly. */
|
|
119
84
|
export interface HarnessCapabilities {
|
|
120
|
-
/** Lower
|
|
121
|
-
* False: a profile carrying subagents is refused at materialization —
|
|
122
|
-
* builders must not add them. */
|
|
85
|
+
/** Lower profile.subagents into native cwd files; false means materialization refuses them. */
|
|
123
86
|
subagents: boolean;
|
|
124
|
-
/** How
|
|
87
|
+
/** How profile.mcp reaches the harness. */
|
|
125
88
|
mcp: 'native' | 'config-file' | 'extension' | 'none';
|
|
126
|
-
/**
|
|
127
|
-
* MCP-carried verbs is checked before MCP tools register (spawn-time
|
|
128
|
-
* validation refuses unknown names); 'runtime' tolerates them. */
|
|
89
|
+
/** Whether tool controls are checked before MCP tools register. */
|
|
129
90
|
toolControls: 'startup' | 'runtime';
|
|
130
|
-
/** cwd-relative directory
|
|
131
|
-
* when the harness has no cwd skill primitive (falls back to inline). */
|
|
91
|
+
/** cwd-relative skill directory, or null when skills must be inlined. */
|
|
132
92
|
skillsDir: string | null;
|
|
133
93
|
}
|
|
134
|
-
/**
|
|
135
|
-
* an entry; the map is exhaustive by construction (tests assert parity). */
|
|
94
|
+
/** Profile-support facts, independent of whether this deployment enables the harness. */
|
|
136
95
|
export declare function harnessCapabilities(harness: Harness): HarnessCapabilities;
|
package/dist/harness/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_HARNESS,
|
|
3
|
+
HarnessSelectionError,
|
|
3
4
|
KNOWN_HARNESSES,
|
|
4
5
|
assertHarnessModelCompatible,
|
|
5
6
|
coerceHarness,
|
|
@@ -11,9 +12,10 @@ import {
|
|
|
11
12
|
resolveSessionHarness,
|
|
12
13
|
snapHarnessToModel,
|
|
13
14
|
snapModelToHarness
|
|
14
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-VBRUG4N5.js";
|
|
15
16
|
export {
|
|
16
17
|
DEFAULT_HARNESS,
|
|
18
|
+
HarnessSelectionError,
|
|
17
19
|
KNOWN_HARNESSES,
|
|
18
20
|
assertHarnessModelCompatible,
|
|
19
21
|
coerceHarness,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Pure session admission. Harness identity is not deployment availability. */
|
|
2
|
+
export interface HarnessAvailability<H extends string> {
|
|
3
|
+
/** Server-owned selection for new sessions. An empty list disables all starts. */
|
|
4
|
+
readonly newSessions: readonly H[];
|
|
5
|
+
/** Includes draining harnesses. Omission uses newSessions, never all known IDs. */
|
|
6
|
+
readonly existingSessions?: readonly H[];
|
|
7
|
+
}
|
|
8
|
+
export interface SessionHarnessSelectionInput<H extends string> {
|
|
9
|
+
sessionHarness?: unknown;
|
|
10
|
+
requested?: unknown;
|
|
11
|
+
workspaceDefault?: unknown;
|
|
12
|
+
fallback?: H;
|
|
13
|
+
/** Omit only when the caller deliberately admits every known harness. */
|
|
14
|
+
availability?: HarnessAvailability<H>;
|
|
15
|
+
}
|
|
16
|
+
export interface SessionHarnessSelection<H extends string> {
|
|
17
|
+
harness: H;
|
|
18
|
+
locked: boolean;
|
|
19
|
+
swapAttempted: boolean;
|
|
20
|
+
}
|
|
21
|
+
type SelectionSource = 'session' | 'requested' | 'workspace' | 'fallback';
|
|
22
|
+
/** An admission failure, never permission to retry on another harness. */
|
|
23
|
+
export declare class HarnessSelectionError extends Error {
|
|
24
|
+
readonly code: 'harness_unknown' | 'harness_unavailable';
|
|
25
|
+
readonly harness: string | null;
|
|
26
|
+
readonly source: SelectionSource;
|
|
27
|
+
constructor(code: 'harness_unknown' | 'harness_unavailable', value: unknown, source: SelectionSource);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Null/undefined mean no choice. Every other value is an explicit choice and must
|
|
31
|
+
* validate. A retired or unrecognized saved identity must never become a new session.
|
|
32
|
+
* Availability is supplied by the authenticated server, not by a request body.
|
|
33
|
+
*/
|
|
34
|
+
export declare function selectSessionHarness<H extends string>(input: SessionHarnessSelectionInput<H>, knownHarnesses: readonly H[], defaultHarness: H): SessionHarnessSelection<H>;
|
|
35
|
+
export {};
|
package/dist/sandbox/index.js
CHANGED
|
@@ -97,7 +97,7 @@ import {
|
|
|
97
97
|
warmBinOf,
|
|
98
98
|
warmWeekdayOf,
|
|
99
99
|
writeProfileFilesToBox
|
|
100
|
-
} from "../chunk-
|
|
100
|
+
} from "../chunk-QW4UOEPH.js";
|
|
101
101
|
import {
|
|
102
102
|
EGRESS_PROXY_RECOVERY_PHASE,
|
|
103
103
|
EGRESS_PROXY_RECOVERY_REQUIRED,
|
|
@@ -136,7 +136,7 @@ import {
|
|
|
136
136
|
workspaceSandboxRecoveryMessage,
|
|
137
137
|
workspaceSandboxRecoveryRecommendedActions
|
|
138
138
|
} from "../chunk-4LBCTFBP.js";
|
|
139
|
-
import "../chunk-
|
|
139
|
+
import "../chunk-VBRUG4N5.js";
|
|
140
140
|
import "../chunk-LWSJK546.js";
|
|
141
141
|
import "../chunk-74WXGIEK.js";
|
|
142
142
|
import "../chunk-6A7MYOUI.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AGENT_PROFILE_EDITOR_SECTIONS,
|
|
3
3
|
AgentProfileEditor
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-GYQTJA6N.js";
|
|
5
|
+
import "../chunk-F2BXNZAH.js";
|
|
6
6
|
import "../chunk-R5K5ML3Q.js";
|
|
7
7
|
import "../chunk-AA4FKEQW.js";
|
|
8
8
|
import "../chunk-RASGKEAL.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-VBRUG4N5.js";
|
|
10
10
|
import "../chunk-B7CL7ENS.js";
|
|
11
11
|
import "../chunk-PV33JZKV.js";
|
|
12
12
|
import "../chunk-AMUXRAUO.js";
|
package/dist/web-react/index.js
CHANGED
|
@@ -166,7 +166,7 @@ import {
|
|
|
166
166
|
AGENT_PROFILE_EDITOR_SECTIONS,
|
|
167
167
|
AgentProfileEditor,
|
|
168
168
|
AgentProfileRegistryError
|
|
169
|
-
} from "../chunk-
|
|
169
|
+
} from "../chunk-GYQTJA6N.js";
|
|
170
170
|
import {
|
|
171
171
|
EvidenceLineageTable,
|
|
172
172
|
ExceptionList,
|
|
@@ -195,7 +195,7 @@ import {
|
|
|
195
195
|
preloadWhenIdle,
|
|
196
196
|
useComposerAttachments,
|
|
197
197
|
useDictation
|
|
198
|
-
} from "../chunk-
|
|
198
|
+
} from "../chunk-PP5KIVHL.js";
|
|
199
199
|
import "../chunk-L7MB2LLM.js";
|
|
200
200
|
import "../chunk-SJWIZT7B.js";
|
|
201
201
|
import {
|
|
@@ -207,7 +207,7 @@ import {
|
|
|
207
207
|
filterAcceptedFiles,
|
|
208
208
|
isAcceptedFileType,
|
|
209
209
|
renamePastedImages
|
|
210
|
-
} from "../chunk-
|
|
210
|
+
} from "../chunk-F2BXNZAH.js";
|
|
211
211
|
import "../chunk-R5K5ML3Q.js";
|
|
212
212
|
import {
|
|
213
213
|
DEFAULT_EFFORT_LEVELS,
|
|
@@ -235,7 +235,7 @@ import "../chunk-FQYAJYPW.js";
|
|
|
235
235
|
import {
|
|
236
236
|
ATTACHMENT_ACCEPT
|
|
237
237
|
} from "../chunk-WTC6AOPL.js";
|
|
238
|
-
import "../chunk-
|
|
238
|
+
import "../chunk-VBRUG4N5.js";
|
|
239
239
|
import "../chunk-B7CL7ENS.js";
|
|
240
240
|
import "../chunk-PV33JZKV.js";
|
|
241
241
|
import "../chunk-AMUXRAUO.js";
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/harness/index.ts"],"sourcesContent":["/**\n * Coding-agent harness selection — taxonomy, coercion, and the session-lock invariant.\n *\n * A \"harness\" is the coding-agent CLI a sandbox drives (opencode / codex /\n * claude-code / …). The shell governs WHICH harness a chat session uses and\n * enforces that a session is LOCKED to the harness it started with — the model\n * may change mid-session, the harness may not (swapping it mid-session would\n * orphan the session's running agent state). Every product otherwise hand-rolls\n * this and hard-codes a single harness; this is the one place the rule lives.\n *\n * Substrate-free: the harness list is derived from `@tangle-network/agent-interface`'s canonical\n * harness enum and mirrors the sandbox SDK's `BackendType` (no sandbox dependency). The consumer\n * owns storage — which harness a workspace defaults to, which one a session locked — and maps the\n * resolved value onto the SDK's `backend.type`.\n *\n * Harness↔model COMPATIBILITY (which models a harness can run, snapping) is NOT defined here — it\n * comes from `@tangle-network/agent-interface`, the single source of truth shared with the\n * sandbox-ui pickers and the cli-bridge backends. This module owns the harness TAXONOMY + the\n * session lock, and its server assertion also checks resolved transport-provider evidence.\n */\n\nimport {\n harnessProviders,\n harnessSupportsModel,\n harnessTypeSchema,\n modelProvider,\n preferredHarnessForModel,\n snapHarnessToModel as aiSnapHarnessToModel,\n snapModelToHarness as aiSnapModelToHarness,\n type HarnessType,\n} from '@tangle-network/agent-interface'\n\n/**\n * Canonical harnesses that are NOT sandbox backends. Each value in `KNOWN_HARNESSES` is dispatched\n * as `backend.type`, so a harness the platform ships no provider adapter for must stay out —\n * offering it would resolve a session onto a runner that cannot start.\n */\nconst NON_BACKEND_HARNESSES = ['gemini'] as const satisfies readonly HarnessType[]\n\nconst nonBackendHarnesses = new Set<string>(NON_BACKEND_HARNESSES)\n\n/** A coding-agent backend this shell can dispatch a session onto. */\nexport type Harness = Exclude<HarnessType, (typeof NON_BACKEND_HARNESSES)[number]>\n\n/**\n * The known coding-agent backends: the canonical harness set minus what has no provider adapter,\n * Derived from `harnessTypeSchema.options` so a harness added upstream reaches this shell without\n * an edit here, and kept structural so this module still needs no sandbox dependency.\n */\nexport const KNOWN_HARNESSES: readonly Harness[] = [\n ...harnessTypeSchema.options.filter(\n (harness): harness is Exclude<HarnessType, (typeof NON_BACKEND_HARNESSES)[number]> =>\n !nonBackendHarnesses.has(harness),\n ),\n]\n\n/** Define the default harness to use for code execution and testing environments */\nexport const DEFAULT_HARNESS: Harness = 'opencode'\n\nconst HARNESS_SET: ReadonlySet<string> = new Set(KNOWN_HARNESSES)\n\n/** Determine if a value is a recognized harness string identifier */\nexport function isHarness(value: unknown): value is Harness {\n return typeof value === 'string' && HARNESS_SET.has(value)\n}\n\n/** Coerce an arbitrary value to a known harness, falling back (default `opencode`). */\nexport function coerceHarness(value: unknown, fallback: Harness = DEFAULT_HARNESS): Harness {\n return isHarness(value) ? value : fallback\n}\n\n/** Resolve input options to determine the appropriate session harness to use */\nexport interface ResolveSessionHarnessInput {\n /** The harness already locked to this session (recorded at its first turn). */\n sessionHarness?: unknown\n /** The harness requested now — a new session's choice, or a turn's attempt to switch. */\n requested?: unknown\n /** The workspace's default harness, used only when starting a fresh session. */\n workspaceDefault?: unknown\n /** Final fallback when nothing else resolves (default `opencode`). */\n fallback?: Harness\n}\n\n/** Represent resolved session state including harness, lock status, and swap attempt flag */\nexport interface ResolvedSessionHarness {\n /** The harness to actually run — the locked one when the session already has it. */\n harness: Harness\n /** True when the session already had a locked harness (this turn did not pick it). */\n locked: boolean\n /** True when `requested` differs from the locked harness — a forbidden mid-session\n * swap the caller should reject or warn on. The lock always wins regardless. */\n swapAttempted: boolean\n}\n\n/**\n * Resolve the harness for a turn, enforcing the session lock.\n *\n * - **Session already started** (`sessionHarness` is a known harness): that harness\n * wins (`locked: true`); a differing `requested` sets `swapAttempted` so the caller\n * can reject the swap. The model is a separate per-turn concern and is unaffected.\n * - **Fresh session**: pick `requested → workspaceDefault → fallback`. The caller\n * persists the result as the session's lock for every subsequent turn.\n */\nexport function resolveSessionHarness(input: ResolveSessionHarnessInput = {}): ResolvedSessionHarness {\n const fallback = input.fallback ?? DEFAULT_HARNESS\n if (isHarness(input.sessionHarness)) {\n const locked = input.sessionHarness\n const swapAttempted = isHarness(input.requested) && input.requested !== locked\n return { harness: locked, locked: true, swapAttempted }\n }\n const harness = coerceHarness(input.requested, coerceHarness(input.workspaceDefault, fallback))\n return { harness, locked: false, swapAttempted: false }\n}\n\n/**\n * An app's harness configuration, read from its deploy configuration (for example the env vars\n * `AGENT_HARNESS` and `AGENT_HARNESS_WORKSPACES`), so no product hard-codes `backend.type`.\n */\nexport interface HarnessConfig {\n /** The app default for new sessions. Unset or blank means {@link DEFAULT_HARNESS}. */\n default?: string\n /** Per-workspace choices: a JSON object mapping a workspace id to a harness. */\n workspaces?: string\n}\n\nfunction configHarness(value: unknown, where: string): Harness {\n if (!isHarness(value)) throw new Error(`${where} names an unknown harness: ${String(value)}`)\n return value\n}\n\n/**\n * The harness a workspace's new sessions start on: its own choice, else the app default, else\n * {@link DEFAULT_HARNESS}. Pass the result to {@link resolveSessionHarness} as `workspaceDefault`\n * so a session that already ran keeps its harness. A value naming an unknown harness throws:\n * a configuration typo must fail loudly, never run a different harness than the one configured.\n */\nexport function configuredHarness(config: HarnessConfig, workspaceId?: string): Harness {\n const appDefault = config.default?.trim()\n ? configHarness(config.default.trim(), 'The harness default') : DEFAULT_HARNESS\n const raw = config.workspaces?.trim()\n if (!raw) return appDefault\n let choices: unknown\n try {\n choices = JSON.parse(raw)\n } catch {\n throw new Error('The per-workspace harness configuration is not JSON')\n }\n if (!choices || typeof choices !== 'object' || Array.isArray(choices)) {\n throw new Error('The per-workspace harness configuration must map workspace ids to harnesses')\n }\n // Every entry is checked, not only the caller's, so one bad entry fails every workspace's\n // first turn after the deploy instead of hiding until that workspace sends a message.\n const parsed = new Map(Object.entries(choices).map(([id, value]) =>\n [id, configHarness(value, `The harness for workspace ${id}`)]))\n return (workspaceId === undefined ? undefined : parsed.get(workspaceId)) ?? appDefault\n}\n\n/**\n * Harness ↔ model compatibility + snapping — delegated to `@tangle-network/agent-interface`.\n *\n * `Harness` is the dispatchable subset of `HarnessType`: this shell drops only canonical harnesses\n * for which the platform has no backend. Compatibility therefore comes entirely from Interface;\n * this package keeps no second harness taxonomy.\n */\n\nexport { modelProvider }\n\n/** A resolved model and the transport provider that will execute it. */\nexport interface HarnessModelSelection {\n model: string\n provider?: string\n}\n\n/** Provider-less ids (sentinels like \"default\", or a session's own config) are\n * compatible everywhere — every harness honors its own configuration. */\nexport function isModelCompatibleWithHarness(harness: Harness, modelId: string): boolean {\n return harnessSupportsModel(harness, modelId)\n}\n\n/** Keep `modelId` when the harness can run it; else the harness's best compatible\n * catalog id (preferred patterns in order, highest version). When nothing in the\n * catalog fits, return the original so the caller sees the incompatibility. */\nexport function snapModelToHarness(harness: Harness, modelId: string, canonicalIds: readonly string[]): string {\n return aiSnapModelToHarness(harness, modelId, canonicalIds)\n}\n\n/** Keep the harness when it can run `modelId`; else the model's native harness\n * (anthropic → claude-code, openai → codex, moonshot → kimi-code), falling back to opencode. */\nexport function snapHarnessToModel(harness: Harness, modelId: string): Harness {\n const snapped = aiSnapHarnessToModel(harness, modelId)\n if (!isHarness(snapped)) {\n throw new Error(\n `Harness \"${harness}\" snapped to \"${snapped}\" for model \"${modelId}\", which this platform has no backend for.`,\n )\n }\n return snapped\n}\n\n/** Fail-loud server guard: throw when a harness is asked to run a model it can't.\n * A resolved model tuple supplies provider evidence when its id is unqualified.\n * Call before dispatching a sandbox turn so a bypassed UI cannot reach the\n * sidecar with an incompatible or unproven pair. */\nexport function assertHarnessModelCompatible(\n harness: Harness,\n selection: string | HarnessModelSelection,\n): void {\n const modelId = typeof selection === 'string' ? selection : selection.model\n const transportProvider = typeof selection === 'string' ? undefined : selection.provider\n const providers = harnessProviders(harness)\n const provider = modelProvider(modelId) ?? transportProvider ?? null\n // An omitted/default model is deliberately left to the harness. Once a\n // caller names a model, however, a vendor-locked harness must have provider\n // evidence. Treating a router provider as compatible with every native CLI\n // is how a bare Gemini model reached Claude Code and produced \"Not logged in\".\n const isDefaultSentinel = modelId.trim() === '' || modelId.trim() === 'default'\n const compatible = isDefaultSentinel\n || providers === null\n || (provider !== null && providers.includes(provider))\n if (!compatible) {\n const native = preferredHarnessForModel(modelId)\n const providerDescription = provider ?? 'unqualified'\n throw new Error(\n `Harness \"${harness}\" cannot run model \"${modelId}\" (provider \"${providerDescription}\"). ` +\n `Use ${native ?? 'a router-backed harness (opencode)'} or an allowed model.`,\n )\n }\n}\n\n// ── Harness capabilities ───────────────────────────────────────────────────\n//\n// The one place a product learns WHAT a harness can express, before it\n// assembles a profile — so incompatible content is never added in the first\n// place instead of being dropped (or refused) after the fact. Facts here are\n// sourced from the platform materializer's behavior (the per-harness subagent\n// lowering, the MCP config channel, the startup control validation) and the\n// placement map; when those change, this table changes with them.\n\n/** What a harness can express, for profile assembly. */\nexport interface HarnessCapabilities {\n /** Lower `profile.subagents` into native cwd files (e.g. `.claude/agents/`).\n * False: a profile carrying subagents is refused at materialization —\n * builders must not add them. */\n subagents: boolean\n /** How `profile.mcp` reaches the harness. */\n mcp: 'native' | 'config-file' | 'extension' | 'none'\n /** When tool controls are validated: 'startup' means an allowlist naming\n * MCP-carried verbs is checked before MCP tools register (spawn-time\n * validation refuses unknown names); 'runtime' tolerates them. */\n toolControls: 'startup' | 'runtime'\n /** cwd-relative directory `resources.skills` materializes into, or null\n * when the harness has no cwd skill primitive (falls back to inline). */\n skillsDir: string | null\n}\n\nconst HARNESS_CAPABILITIES: Record<Harness, HarnessCapabilities> = {\n 'claude-code': { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.claude/skills' },\n nanoclaw: { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.claude/skills' },\n opencode: { subagents: true, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.opencode/skills' },\n codex: { subagents: true, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.codex/skills' },\n 'kimi-code': { subagents: false, mcp: 'config-file', toolControls: 'runtime', skillsDir: '.kimi/skills' },\n pi: { subagents: false, mcp: 'extension', toolControls: 'startup', skillsDir: '.pi/skills' },\n prime: { subagents: true, mcp: 'native', toolControls: 'runtime', skillsDir: '.prime/agent/skills' },\n hermes: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n openclaw: { subagents: false, mcp: 'config-file', toolControls: 'runtime', skillsDir: 'skills' },\n // Harnesses the platform placement map does not bridge yet: conservative\n // facts — assert nothing they have not proven. Each upgrades to its real\n // facts when the materializer grows support for it.\n acp: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n amp: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n 'factory-droids': { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n forge: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n cursor: { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n 'cli-base': { subagents: false, mcp: 'native', toolControls: 'runtime', skillsDir: null },\n}\n\n/** The capability facts for `harness`. Every harness in KNOWN_HARNESSES has\n * an entry; the map is exhaustive by construction (tests assert parity). */\nexport function harnessCapabilities(harness: Harness): HarnessCapabilities {\n const caps = HARNESS_CAPABILITIES[harness]\n if (!caps) throw new Error(`harnessCapabilities: no capability facts for \"${harness}\"`)\n return caps\n}\n"],"mappings":";AAqBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,OAEjB;AAOP,IAAM,wBAAwB,CAAC,QAAQ;AAEvC,IAAM,sBAAsB,IAAI,IAAY,qBAAqB;AAU1D,IAAM,kBAAsC;AAAA,EACjD,GAAG,kBAAkB,QAAQ;AAAA,IAC3B,CAAC,YACC,CAAC,oBAAoB,IAAI,OAAO;AAAA,EACpC;AACF;AAGO,IAAM,kBAA2B;AAExC,IAAM,cAAmC,IAAI,IAAI,eAAe;AAGzD,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAY,YAAY,IAAI,KAAK;AAC3D;AAGO,SAAS,cAAc,OAAgB,WAAoB,iBAA0B;AAC1F,SAAO,UAAU,KAAK,IAAI,QAAQ;AACpC;AAkCO,SAAS,sBAAsB,QAAoC,CAAC,GAA2B;AACpG,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,UAAU,MAAM,cAAc,GAAG;AACnC,UAAM,SAAS,MAAM;AACrB,UAAM,gBAAgB,UAAU,MAAM,SAAS,KAAK,MAAM,cAAc;AACxE,WAAO,EAAE,SAAS,QAAQ,QAAQ,MAAM,cAAc;AAAA,EACxD;AACA,QAAM,UAAU,cAAc,MAAM,WAAW,cAAc,MAAM,kBAAkB,QAAQ,CAAC;AAC9F,SAAO,EAAE,SAAS,QAAQ,OAAO,eAAe,MAAM;AACxD;AAaA,SAAS,cAAc,OAAgB,OAAwB;AAC7D,MAAI,CAAC,UAAU,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,8BAA8B,OAAO,KAAK,CAAC,EAAE;AAC5F,SAAO;AACT;AAQO,SAAS,kBAAkB,QAAuB,aAA+B;AACtF,QAAM,aAAa,OAAO,SAAS,KAAK,IACpC,cAAc,OAAO,QAAQ,KAAK,GAAG,qBAAqB,IAAI;AAClE,QAAM,MAAM,OAAO,YAAY,KAAK;AACpC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACJ,MAAI;AACF,cAAU,KAAK,MAAM,GAAG;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,MAAI,CAAC,WAAW,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO,GAAG;AACrE,UAAM,IAAI,MAAM,6EAA6E;AAAA,EAC/F;AAGA,QAAM,SAAS,IAAI,IAAI,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,KAAK,MAC5D,CAAC,IAAI,cAAc,OAAO,6BAA6B,EAAE,EAAE,CAAC,CAAC,CAAC;AAChE,UAAQ,gBAAgB,SAAY,SAAY,OAAO,IAAI,WAAW,MAAM;AAC9E;AAoBO,SAAS,6BAA6B,SAAkB,SAA0B;AACvF,SAAO,qBAAqB,SAAS,OAAO;AAC9C;AAKO,SAAS,mBAAmB,SAAkB,SAAiB,cAAyC;AAC7G,SAAO,qBAAqB,SAAS,SAAS,YAAY;AAC5D;AAIO,SAAS,mBAAmB,SAAkB,SAA0B;AAC7E,QAAM,UAAU,qBAAqB,SAAS,OAAO;AACrD,MAAI,CAAC,UAAU,OAAO,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,YAAY,OAAO,iBAAiB,OAAO,gBAAgB,OAAO;AAAA,IACpE;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,6BACd,SACA,WACM;AACN,QAAM,UAAU,OAAO,cAAc,WAAW,YAAY,UAAU;AACtE,QAAM,oBAAoB,OAAO,cAAc,WAAW,SAAY,UAAU;AAChF,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,WAAW,cAAc,OAAO,KAAK,qBAAqB;AAKhE,QAAM,oBAAoB,QAAQ,KAAK,MAAM,MAAM,QAAQ,KAAK,MAAM;AACtE,QAAM,aAAa,qBACd,cAAc,QACb,aAAa,QAAQ,UAAU,SAAS,QAAQ;AACtD,MAAI,CAAC,YAAY;AACf,UAAM,SAAS,yBAAyB,OAAO;AAC/C,UAAM,sBAAsB,YAAY;AACxC,UAAM,IAAI;AAAA,MACR,YAAY,OAAO,uBAAuB,OAAO,gBAAgB,mBAAmB,WAC3E,UAAU,oCAAoC;AAAA,IACzD;AAAA,EACF;AACF;AA4BA,IAAM,uBAA6D;AAAA,EACjE,eAAe,EAAE,WAAW,MAAM,KAAK,UAAU,cAAc,WAAW,WAAW,iBAAiB;AAAA,EACtG,UAAU,EAAE,WAAW,MAAM,KAAK,UAAU,cAAc,WAAW,WAAW,iBAAiB;AAAA,EACjG,UAAU,EAAE,WAAW,MAAM,KAAK,eAAe,cAAc,WAAW,WAAW,mBAAmB;AAAA,EACxG,OAAO,EAAE,WAAW,MAAM,KAAK,eAAe,cAAc,WAAW,WAAW,gBAAgB;AAAA,EAClG,aAAa,EAAE,WAAW,OAAO,KAAK,eAAe,cAAc,WAAW,WAAW,eAAe;AAAA,EACxG,IAAI,EAAE,WAAW,OAAO,KAAK,aAAa,cAAc,WAAW,WAAW,aAAa;AAAA,EAC3F,OAAO,EAAE,WAAW,MAAM,KAAK,UAAU,cAAc,WAAW,WAAW,sBAAsB;AAAA,EACnG,QAAQ,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EACpF,UAAU,EAAE,WAAW,OAAO,KAAK,eAAe,cAAc,WAAW,WAAW,SAAS;AAAA;AAAA;AAAA;AAAA,EAI/F,KAAK,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EACjF,KAAK,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EACjF,kBAAkB,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EAC9F,OAAO,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EACnF,QAAQ,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAAA,EACpF,YAAY,EAAE,WAAW,OAAO,KAAK,UAAU,cAAc,WAAW,WAAW,KAAK;AAC1F;AAIO,SAAS,oBAAoB,SAAuC;AACzE,QAAM,OAAO,qBAAqB,OAAO;AACzC,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,iDAAiD,OAAO,GAAG;AACtF,SAAO;AACT;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|