@tangle-network/agent-app 0.1.5 → 0.1.6
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/chunk-SD2H4FWY.js +43 -0
- package/dist/chunk-SD2H4FWY.js.map +1 -0
- package/dist/harness/index.d.ts +54 -0
- package/dist/harness/index.js +15 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -3
- package/package.json +6 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/harness/index.ts
|
|
2
|
+
var KNOWN_HARNESSES = [
|
|
3
|
+
"opencode",
|
|
4
|
+
"claude-code",
|
|
5
|
+
"kimi-code",
|
|
6
|
+
"codex",
|
|
7
|
+
"amp",
|
|
8
|
+
"factory-droids",
|
|
9
|
+
"pi",
|
|
10
|
+
"hermes",
|
|
11
|
+
"forge",
|
|
12
|
+
"openclaw",
|
|
13
|
+
"acp",
|
|
14
|
+
"cursor",
|
|
15
|
+
"cli-base"
|
|
16
|
+
];
|
|
17
|
+
var DEFAULT_HARNESS = "opencode";
|
|
18
|
+
var HARNESS_SET = new Set(KNOWN_HARNESSES);
|
|
19
|
+
function isHarness(value) {
|
|
20
|
+
return typeof value === "string" && HARNESS_SET.has(value);
|
|
21
|
+
}
|
|
22
|
+
function coerceHarness(value, fallback = DEFAULT_HARNESS) {
|
|
23
|
+
return isHarness(value) ? value : fallback;
|
|
24
|
+
}
|
|
25
|
+
function resolveSessionHarness(input = {}) {
|
|
26
|
+
const fallback = input.fallback ?? DEFAULT_HARNESS;
|
|
27
|
+
if (isHarness(input.sessionHarness)) {
|
|
28
|
+
const locked = input.sessionHarness;
|
|
29
|
+
const swapAttempted = isHarness(input.requested) && input.requested !== locked;
|
|
30
|
+
return { harness: locked, locked: true, swapAttempted };
|
|
31
|
+
}
|
|
32
|
+
const harness = coerceHarness(input.requested, coerceHarness(input.workspaceDefault, fallback));
|
|
33
|
+
return { harness, locked: false, swapAttempted: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
KNOWN_HARNESSES,
|
|
38
|
+
DEFAULT_HARNESS,
|
|
39
|
+
isHarness,
|
|
40
|
+
coerceHarness,
|
|
41
|
+
resolveSessionHarness
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=chunk-SD2H4FWY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 mirrors the sandbox SDK's `BackendType` as a\n * plain string union (no sandbox dependency). The consumer owns storage — which\n * harness a workspace defaults to, which one a session locked — and maps the\n * resolved value onto the SDK's `backend.type`.\n */\n\n/** The known coding-agent backends. Mirrors `@tangle-network/sandbox`'s\n * `BackendType`; kept structural so this module needs no sandbox dependency. */\nexport const KNOWN_HARNESSES = [\n 'opencode',\n 'claude-code',\n 'kimi-code',\n 'codex',\n 'amp',\n 'factory-droids',\n 'pi',\n 'hermes',\n 'forge',\n 'openclaw',\n 'acp',\n 'cursor',\n 'cli-base',\n] as const\n\nexport type Harness = (typeof KNOWN_HARNESSES)[number]\n\nexport const DEFAULT_HARNESS: Harness = 'opencode'\n\nconst HARNESS_SET: ReadonlySet<string> = new Set(KNOWN_HARNESSES)\n\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\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\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"],"mappings":";AAkBO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,kBAA2B;AAExC,IAAM,cAAmC,IAAI,IAAI,eAAe;AAEzD,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;AAgCO,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;","names":[]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coding-agent harness selection — taxonomy, coercion, and the session-lock invariant.
|
|
3
|
+
*
|
|
4
|
+
* A "harness" is the coding-agent CLI a sandbox drives (opencode / codex /
|
|
5
|
+
* claude-code / …). The shell governs WHICH harness a chat session uses and
|
|
6
|
+
* enforces that a session is LOCKED to the harness it started with — the model
|
|
7
|
+
* may change mid-session, the harness may not (swapping it mid-session would
|
|
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.
|
|
10
|
+
*
|
|
11
|
+
* Substrate-free: the harness list mirrors the sandbox SDK's `BackendType` as a
|
|
12
|
+
* plain string union (no sandbox dependency). The consumer owns storage — which
|
|
13
|
+
* harness a workspace defaults to, which one a session locked — and maps the
|
|
14
|
+
* resolved value onto the SDK's `backend.type`.
|
|
15
|
+
*/
|
|
16
|
+
/** The known coding-agent backends. Mirrors `@tangle-network/sandbox`'s
|
|
17
|
+
* `BackendType`; kept structural so this module needs no sandbox dependency. */
|
|
18
|
+
declare const KNOWN_HARNESSES: readonly ["opencode", "claude-code", "kimi-code", "codex", "amp", "factory-droids", "pi", "hermes", "forge", "openclaw", "acp", "cursor", "cli-base"];
|
|
19
|
+
type Harness = (typeof KNOWN_HARNESSES)[number];
|
|
20
|
+
declare const DEFAULT_HARNESS: Harness;
|
|
21
|
+
declare function isHarness(value: unknown): value is Harness;
|
|
22
|
+
/** Coerce an arbitrary value to a known harness, falling back (default `opencode`). */
|
|
23
|
+
declare function coerceHarness(value: unknown, fallback?: Harness): Harness;
|
|
24
|
+
interface ResolveSessionHarnessInput {
|
|
25
|
+
/** The harness already locked to this session (recorded at its first turn). */
|
|
26
|
+
sessionHarness?: unknown;
|
|
27
|
+
/** The harness requested now — a new session's choice, or a turn's attempt to switch. */
|
|
28
|
+
requested?: unknown;
|
|
29
|
+
/** The workspace's default harness, used only when starting a fresh session. */
|
|
30
|
+
workspaceDefault?: unknown;
|
|
31
|
+
/** Final fallback when nothing else resolves (default `opencode`). */
|
|
32
|
+
fallback?: Harness;
|
|
33
|
+
}
|
|
34
|
+
interface ResolvedSessionHarness {
|
|
35
|
+
/** The harness to actually run — the locked one when the session already has it. */
|
|
36
|
+
harness: Harness;
|
|
37
|
+
/** True when the session already had a locked harness (this turn did not pick it). */
|
|
38
|
+
locked: boolean;
|
|
39
|
+
/** True when `requested` differs from the locked harness — a forbidden mid-session
|
|
40
|
+
* swap the caller should reject or warn on. The lock always wins regardless. */
|
|
41
|
+
swapAttempted: boolean;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve the harness for a turn, enforcing the session lock.
|
|
45
|
+
*
|
|
46
|
+
* - **Session already started** (`sessionHarness` is a known harness): that harness
|
|
47
|
+
* wins (`locked: true`); a differing `requested` sets `swapAttempted` so the caller
|
|
48
|
+
* can reject the swap. The model is a separate per-turn concern and is unaffected.
|
|
49
|
+
* - **Fresh session**: pick `requested → workspaceDefault → fallback`. The caller
|
|
50
|
+
* persists the result as the session's lock for every subsequent turn.
|
|
51
|
+
*/
|
|
52
|
+
declare function resolveSessionHarness(input?: ResolveSessionHarnessInput): ResolvedSessionHarness;
|
|
53
|
+
|
|
54
|
+
export { DEFAULT_HARNESS, type Harness, KNOWN_HARNESSES, type ResolveSessionHarnessInput, type ResolvedSessionHarness, coerceHarness, isHarness, resolveSessionHarness };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_HARNESS,
|
|
3
|
+
KNOWN_HARNESSES,
|
|
4
|
+
coerceHarness,
|
|
5
|
+
isHarness,
|
|
6
|
+
resolveSessionHarness
|
|
7
|
+
} from "../chunk-SD2H4FWY.js";
|
|
8
|
+
export {
|
|
9
|
+
DEFAULT_HARNESS,
|
|
10
|
+
KNOWN_HARNESSES,
|
|
11
|
+
coerceHarness,
|
|
12
|
+
isHarness,
|
|
13
|
+
resolveSessionHarness
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { AppToolLoopOptions, LoopEvent, LoopToolCall, OpenAICompatStreamTurnOpti
|
|
|
6
6
|
export { createTokenRecallChecker, producedFromToolEvents } from './eval/index.js';
|
|
7
7
|
export { KnowledgeRequirementSpec, KnowledgeSignal, KnowledgeStateAccessor, SatisfiedByRule, buildKnowledgeRequirements, deriveSignals } from './knowledge/index.js';
|
|
8
8
|
export { CreateKnowledgeLoopDeps, KnowledgeCandidate, KnowledgeDecider, KnowledgeDeciderInput, KnowledgeDecision, KnowledgeGateVerdict, KnowledgeLoop, KnowledgeLoopDriver, createKnowledgeLoop, createReviewerDecider, reviewCandidate } from './knowledge-loop/index.js';
|
|
9
|
+
export { DEFAULT_HARNESS, Harness, KNOWN_HARNESSES, ResolveSessionHarnessInput, ResolvedSessionHarness, coerceHarness, isHarness, resolveSessionHarness } from './harness/index.js';
|
|
9
10
|
export { AgentAppConfig, AgentDelegationConfig, AgentIdentityConfig, AgentIntegrationsConfig, AgentKnowledgeConfig, AgentTaxonomyConfig, AgentUiConfig, KnowledgeLoopConfig, KnowledgeSourceSpec, agentAppConfigJsonSchema, defineAgentApp } from './config/index.js';
|
|
10
11
|
export { D1Like, D1PreparedLike, DrizzleColumnLike, DrizzleSqliteCoreLike, PRESET_MIGRATION_SQL, PRESET_TABLES, PresetBillingOptions, PresetKnowledgeAccessorOptions, PresetToolHandlerOptions, VaultKv, createD1KnowledgeStateAccessor, createPresetDrizzleSchema, createPresetFieldCrypto, createPresetToolHandlers, createPresetWorkspaceKeyManager, createPresetWorkspaceKeyStore } from './preset-cloudflare/index.js';
|
|
11
12
|
export { KeyCrypto, KeyProvisioner, PlanLimit, PlatformBalanceInfo, PlatformBalanceManager, PlatformBalanceManagerOptions, PlatformBillingClient, PlatformIdentity, PlatformProductUsage, SharedBillingState, WorkspaceKeyManager, WorkspaceKeyManagerOptions, WorkspaceKeyRecord, WorkspaceKeyStore, WorkspaceModelKeyUsage, createPlatformBalanceManager, createWorkspaceKeyManager } from './billing/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
redactForIngestion
|
|
3
|
+
} from "./chunk-C5CREGT2.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_HARNESS,
|
|
6
|
+
KNOWN_HARNESSES,
|
|
7
|
+
coerceHarness,
|
|
8
|
+
isHarness,
|
|
9
|
+
resolveSessionHarness
|
|
10
|
+
} from "./chunk-SD2H4FWY.js";
|
|
1
11
|
import {
|
|
2
12
|
agentAppConfigJsonSchema,
|
|
3
13
|
defineAgentApp
|
|
@@ -56,9 +66,6 @@ import {
|
|
|
56
66
|
parseJsonObjectBody,
|
|
57
67
|
requireString
|
|
58
68
|
} from "./chunk-CN75FIPT.js";
|
|
59
|
-
import {
|
|
60
|
-
redactForIngestion
|
|
61
|
-
} from "./chunk-C5CREGT2.js";
|
|
62
69
|
import {
|
|
63
70
|
APP_TOOL_NAMES,
|
|
64
71
|
DEFAULT_APP_TOOL_PATHS,
|
|
@@ -115,11 +122,13 @@ import {
|
|
|
115
122
|
export {
|
|
116
123
|
APP_TOOL_NAMES,
|
|
117
124
|
DEFAULT_APP_TOOL_PATHS,
|
|
125
|
+
DEFAULT_HARNESS,
|
|
118
126
|
DEFAULT_HEADER_NAMES,
|
|
119
127
|
DEFAULT_TANGLE_ROUTER_BASE_URL,
|
|
120
128
|
DELEGATION_MCP_SERVER_KEY,
|
|
121
129
|
DELEGATION_TOOLS,
|
|
122
130
|
HubExecClient,
|
|
131
|
+
KNOWN_HARNESSES,
|
|
123
132
|
PRESET_MIGRATION_SQL,
|
|
124
133
|
PRESET_TABLES,
|
|
125
134
|
ToolInputError,
|
|
@@ -136,6 +145,7 @@ export {
|
|
|
136
145
|
buildKnowledgeRequirements,
|
|
137
146
|
buildUserTextParts,
|
|
138
147
|
checkRateLimit,
|
|
148
|
+
coerceHarness,
|
|
139
149
|
createAppToolRuntimeExecutor,
|
|
140
150
|
createBrokerTokenProvider,
|
|
141
151
|
createCapabilityToken,
|
|
@@ -173,6 +183,7 @@ export {
|
|
|
173
183
|
handleAppToolRequest,
|
|
174
184
|
invokeIntegrationHub,
|
|
175
185
|
isAppToolName,
|
|
186
|
+
isHarness,
|
|
176
187
|
mergePersistedPart,
|
|
177
188
|
messageHasTurnId,
|
|
178
189
|
normalizeClientTurnId,
|
|
@@ -187,6 +198,7 @@ export {
|
|
|
187
198
|
requireString,
|
|
188
199
|
resolveChatTurn,
|
|
189
200
|
resolveIntegrationAction,
|
|
201
|
+
resolveSessionHarness,
|
|
190
202
|
resolveTangleModelConfig,
|
|
191
203
|
resolveToolId,
|
|
192
204
|
resolveToolName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"packageManager": "pnpm@10.33.4",
|
|
5
5
|
"description": "Application-shell framework for Tangle agent products: a bounded tool loop, the structured agent→app tool side channel, integration-hub client, per-workspace billing, and crypto — composed over the Tangle agent substrate through typed seams.",
|
|
6
6
|
"keywords": [
|
|
@@ -76,6 +76,11 @@
|
|
|
76
76
|
"import": "./dist/knowledge-loop/index.js",
|
|
77
77
|
"default": "./dist/knowledge-loop/index.js"
|
|
78
78
|
},
|
|
79
|
+
"./harness": {
|
|
80
|
+
"types": "./dist/harness/index.d.ts",
|
|
81
|
+
"import": "./dist/harness/index.js",
|
|
82
|
+
"default": "./dist/harness/index.js"
|
|
83
|
+
},
|
|
79
84
|
"./preset-cloudflare": {
|
|
80
85
|
"types": "./dist/preset-cloudflare/index.d.ts",
|
|
81
86
|
"import": "./dist/preset-cloudflare/index.js",
|