@tangle-network/agent-app 0.45.50 → 0.45.52

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.
@@ -1612,19 +1612,23 @@ declare function syncSandboxMemberAdd(box: SandboxInstance, seam: MemberSyncSeam
1612
1612
  declare function syncSandboxMemberRemove(box: SandboxInstance, userId: string): Promise<Outcome<void>>;
1613
1613
  /** Synchronize a sandbox member's role by updating permissions based on the provided role mapping */
1614
1614
  declare function syncSandboxMemberRole(box: SandboxInstance, seam: MemberSyncSeam, userId: string, role: string): Promise<Outcome<void>>;
1615
- /** Define methods to create, update, retrieve, and delete secrets asynchronously */
1615
+ /**
1616
+ * Write-only secret port: create, replace, and delete a secret by name.
1617
+ *
1618
+ * There is no read method. The platform's secrets API returns names and
1619
+ * timestamps only — a stored value is never served back, it is injected into
1620
+ * the sandbox as an environment variable. A `get` here could only ever return
1621
+ * a value this process already held, so the port does not offer one.
1622
+ */
1616
1623
  interface SecretStore {
1617
1624
  create: (name: string, value: string) => Promise<void>;
1618
1625
  update: (name: string, value: string) => Promise<void>;
1619
- get: (name: string) => Promise<string>;
1620
1626
  delete: (name: string) => Promise<void>;
1621
1627
  }
1622
1628
  /** Resolve a SecretStore interface using the provided SandboxRuntimeConfig shell */
1623
1629
  declare function secretStoreFromClient(shell: SandboxRuntimeConfig): SecretStore;
1624
- /** Resolve storing a secret by creating or updating it in the given SecretStore */
1630
+ /** Resolve storing a secret by creating it, or replacing it when it exists */
1625
1631
  declare function storeSecret(store: SecretStore, name: string, value: string): Promise<Outcome<void>>;
1626
- /** Resolve a secret value from the store by its name and return the outcome asynchronously */
1627
- declare function readSecret(store: SecretStore, name: string): Promise<Outcome<string>>;
1628
1632
  /** Delete a secret by name from the given secret store and return the operation outcome */
1629
1633
  declare function deleteSecret(store: SecretStore, name: string): Promise<Outcome<void>>;
1630
1634
  /** Represent a token with its expiration date and associated scope */
@@ -1667,4 +1671,4 @@ declare function isTerminalPromptEvent(event: unknown): boolean;
1667
1671
  /** Resolve the interactive question text from a structured event or return null if none found */
1668
1672
  declare function detectInteractiveQuestion(event: unknown): string | null;
1669
1673
 
1670
- export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_PROVISION_TIMEOUT_MS, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_PHASE, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, PYPI_EGRESS_DOMAINS, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxProvisionedObservation, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxSpendHooks, type SandboxStepTransition, type SandboxStreamEvent, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, WORKSPACE_SANDBOX_HOST_EXHAUSTED, WORKSPACE_SANDBOX_MISSING, WORKSPACE_SANDBOX_RECOVERY_ACTIONS, WORKSPACE_SANDBOX_RECOVERY_CODES, WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS, WORKSPACE_SANDBOX_UNRECOVERABLE, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRecoveryAction, type WorkspaceSandboxRecoveryCode, type WorkspaceSandboxRecoveryDecision, type WorkspaceSandboxRecoveryManager, WorkspaceSandboxRecoveryRequiredError, type WorkspaceSandboxRecoveryState, type WorkspaceSandboxRecoveryStore, type WorkspaceSandboxSnapshot, type WorkspaceSandboxSnapshotAssessment, type WorkspaceSandboxSnapshotAvailability, type WorkspaceSandboxSnapshotFreshness, type WriteProfileFilesOptions, adaptSandboxStream, assertEnvWithinLimits, assertProvisionPayloadWithinCap, assessWorkspaceSandboxSnapshot, attachReasoningEffort, buildAppToolMcpServers, buildProductEgressPolicy, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, createWorkspaceSandboxRecoveryManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isEgressProxyRecoveryRequiredError, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, isWorkspaceSandboxRecoveryAction, isWorkspaceSandboxRecoveryCode, isWorkspaceSandboxRecoveryState, isWorkspaceSandboxSnapshotRestoreError, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, preferredWorkspaceSandboxRecoveryBoxKey, readSandboxBinaryBytes, readSecret, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, shouldRestoreWorkspaceSandboxRecovery, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, workspaceSandboxRecoveryDiagnostic, workspaceSandboxRecoveryFromError, workspaceSandboxRecoveryMessage, workspaceSandboxRecoveryRecommendedActions, writeProfileFilesToBox };
1674
+ export { type AppToolDescriptor, type BuildAppToolMcpServersOptions, type BuildSandboxToolFileMountsOptions, type D1PrewarmClaimStoreOptions, DEFAULT_PREWARM_CLAIM_TABLE, DEFAULT_PROVISION_TIMEOUT_MS, DEFAULT_SANDBOX_RESOURCES, DEFAULT_SIDECAR_PROCESS_PATTERN, type DriveSandboxTurnOptions, EGRESS_PROXY_RECOVERY_PHASE, EGRESS_PROXY_RECOVERY_REQUIRED, ENV_TOTAL_MAX_BYTES, ENV_VALUE_MAX_BYTES, type EnsureWorkspaceSandboxOptions, type LivenessProbeConfig, type MemberSyncSeam, type ModelSelection, type ModelSelectionError, type ModelSelectionFailure, type ModelSelectionSource, type Outcome, PREWARM_CLAIM_TABLE_DDL, PROVISION_PAYLOAD_MAX_BYTES, PYPI_EGRESS_DOMAINS, type PeekWorkspaceSandboxOutcome, type PrewarmClaimD1Like, type PrewarmClaimStore, type PrewarmDecision, type PrewarmEvent, type PrewarmOutcome, type PrewarmResult, type ProfileComposeOptions, type PromptInputPart, type ProviderResolutionConfig, type ProvisionPayloadSections, type ProvisionProfileSection, type ResolveSandboxClientCredentialsOptions, type ResolvedModel, type SafeSandboxErrorCause, type SafeSandboxErrorDiagnostics, type SandboxBuildContext, type SandboxClientCredentials, type SandboxCredentialEnvironment, SandboxEgressPolicyMismatchError, type SandboxEgressPolicySource, type SandboxExecChannel, type SandboxExecOptions, type SandboxExistingBoxStage, type SandboxFileBytesOutcome, type SandboxFileSizeOutcome, SandboxModelResolutionError, type SandboxPermissionLevel, type SandboxPrewarmScope, type SandboxPrewarmer, type SandboxPrewarmerOptions, type SandboxProvisionedObservation, type SandboxReadiness, SandboxRecoveryFailedError, type SandboxRecoveryPhase, type SandboxResourceConfig, type SandboxRestoreSpec, SandboxRuntimeAuthRefreshError, type SandboxRuntimeConfig, type SandboxScope, type SandboxSpendHooks, type SandboxStepTransition, type SandboxStreamEvent, type SandboxTerminalConnectionRouteOptions, type SandboxToolPathOptions, type SandboxToolSpec, type ScopedTokenResult, type SecretStore, type StoppedSandboxResumeFailure, type StoppedSandboxResumeRecovery, type StreamSandboxPromptOptions, type TerminalConnectionBoxLike, WORKSPACE_SANDBOX_HOST_EXHAUSTED, WORKSPACE_SANDBOX_MISSING, WORKSPACE_SANDBOX_RECOVERY_ACTIONS, WORKSPACE_SANDBOX_RECOVERY_CODES, WORKSPACE_SANDBOX_SNAPSHOT_MAX_AGE_MS, WORKSPACE_SANDBOX_UNRECOVERABLE, type WorkspaceSandboxEnsureContext, type WorkspaceSandboxInstanceLike, type WorkspaceSandboxManager, type WorkspaceSandboxManagerOptions, type WorkspaceSandboxRecoveryAction, type WorkspaceSandboxRecoveryCode, type WorkspaceSandboxRecoveryDecision, type WorkspaceSandboxRecoveryManager, WorkspaceSandboxRecoveryRequiredError, type WorkspaceSandboxRecoveryState, type WorkspaceSandboxRecoveryStore, type WorkspaceSandboxSnapshot, type WorkspaceSandboxSnapshotAssessment, type WorkspaceSandboxSnapshotAvailability, type WorkspaceSandboxSnapshotFreshness, type WriteProfileFilesOptions, adaptSandboxStream, assertEnvWithinLimits, assertProvisionPayloadWithinCap, assessWorkspaceSandboxSnapshot, attachReasoningEffort, buildAppToolMcpServers, buildProductEgressPolicy, buildSandboxToolFileMounts, buildSandboxToolPathSetupScript, classifySeveredStream, collectSandboxPromptText, createD1PrewarmClaimStore, createSandboxPrewarmer, createSandboxTerminalConnectionRoute, createWorkspaceSandboxManager, createWorkspaceSandboxRecoveryManager, deferredCorpusHash, deleteSecret, detectInteractiveQuestion, driveSandboxTurn, ensureWorkspaceSandbox, flattenHistory, formatSandboxProvisioningSupportDetails, formatSandboxProvisioningUserMessage, getClient, isEgressProxyRecoveryRequiredError, isSandboxApiBearerAuthFailure, isSandboxApiSandboxMissingFailure, isSandboxAuthFailure, isSandboxHostCapacityFailure, isTerminalPromptEvent, isWorkspaceSandboxRecoveryAction, isWorkspaceSandboxRecoveryCode, isWorkspaceSandboxRecoveryState, isWorkspaceSandboxSnapshotRestoreError, mergeExtraMcp, mergeHistoryIntoParts, mintSandboxScopedToken, peekWorkspaceSandbox, preferredWorkspaceSandboxRecoveryBoxKey, readSandboxBinaryBytes, requireTransportableModel, resetClientCache, resolveModel, resolveModelSelection, resolveSandboxClientCredentials, runSandboxPrompt, runSandboxToolPathSetup, sandboxToolBinDir, sandboxToolPath, sandboxToolRootDir, secretStoreFromClient, serializeSandboxProvisioningError, shellQuote, shouldRestoreWorkspaceSandboxRecovery, splitDeferredProfileFiles, statSandboxFileSize, storeSecret, streamSandboxPrompt, syncSandboxMemberAdd, syncSandboxMemberRemove, syncSandboxMemberRole, workspaceSandboxRecoveryDiagnostic, workspaceSandboxRecoveryFromError, workspaceSandboxRecoveryMessage, workspaceSandboxRecoveryRecommendedActions, writeProfileFilesToBox };
@@ -62,7 +62,6 @@ import {
62
62
  peekWorkspaceSandbox,
63
63
  preferredWorkspaceSandboxRecoveryBoxKey,
64
64
  readSandboxBinaryBytes,
65
- readSecret,
66
65
  requireTransportableModel,
67
66
  resetClientCache,
68
67
  resolveModel,
@@ -89,7 +88,7 @@ import {
89
88
  workspaceSandboxRecoveryMessage,
90
89
  workspaceSandboxRecoveryRecommendedActions,
91
90
  writeProfileFilesToBox
92
- } from "../chunk-RRH23CGS.js";
91
+ } from "../chunk-MA2RIIIH.js";
93
92
  import "../chunk-TH7L265V.js";
94
93
  import "../chunk-LWSJK546.js";
95
94
  import "../chunk-P5PIAQVS.js";
@@ -162,7 +161,6 @@ export {
162
161
  peekWorkspaceSandbox,
163
162
  preferredWorkspaceSandboxRecoveryBoxKey,
164
163
  readSandboxBinaryBytes,
165
- readSecret,
166
164
  requireTransportableModel,
167
165
  resetClientCache,
168
166
  resolveModel,
@@ -15,6 +15,7 @@ var HARNESS_BRIDGE = {
15
15
  "kimi-code": "kimi-code",
16
16
  codex: "codex",
17
17
  pi: "pi",
18
+ prime: "prime",
18
19
  hermes: "hermes",
19
20
  openclaw: "openclaw"
20
21
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/skills-placement/index.ts"],"sourcesContent":["/**\n * Harness-native skill directory resolution — the one place agent-app binds\n * to the platform's authoritative per-harness skill-dir map.\n *\n * `../skills` renders skill CONTENT (parse, tier-filter, `inline`/`mounted`\n * delivery) but deliberately stops short of naming WHICH cwd path a `mounted`\n * skill lands at on a given harness — that mapping is owned by the platform\n * materializer (`@tangle-network/agent-profile-materialize`'s\n * `skillDirForHarness`), not by app-shell. This subpath exists so no product\n * — and no other agent-app module — ever writes a skill path literal\n * (`~/.claude/skills/...`, `.opencode/skills`, ...) of its own; it bridges\n * agent-app's `Harness` taxonomy onto the platform's `HarnessId` and asks the\n * platform for the answer.\n *\n * Requires the OPTIONAL peer `@tangle-network/agent-profile-materialize`.\n * Products that don't install it simply don't import this subpath — every\n * other agent-app skills surface (`../skills`, `ProfileChannels.skillRefs`)\n * works without it, falling back to `inline` delivery.\n */\n\nimport { KNOWN_HARNESSES, type Harness } from '../harness/index'\nimport { skillDirForHarness, type HarnessId } from '@tangle-network/agent-profile-materialize'\nimport { composeSkills, renderInlineSkills, type ComposedSkills, type SkillEntry } from '../skills/index'\n\n/** agent-app `Harness` -> platform `HarnessId`, identity-mapped for exactly\n * the harnesses the platform map covers. Harnesses absent here (`amp`,\n * `factory-droids`, `forge`, `acp`, `cursor`, `cli-base`) resolve to `null` —\n * {@link composeSkillsForHarness} refuses by default rather than silently\n * delivering `inline`; a caller that genuinely needs `inline` on one of\n * these opts in explicitly (`onNoSkillDir: 'inline'`). `cursor`'s adapter\n * supports `resources.skills` bespokely but isn't in the platform map yet;\n * treating it as unbridged is the safe posture until the map covers it,\n * rather than guessing its cwd skill dir here. */\nconst HARNESS_BRIDGE: Partial<Record<Harness, HarnessId>> = {\n opencode: 'opencode',\n 'claude-code': 'claude-code',\n nanoclaw: 'nanoclaw',\n 'kimi-code': 'kimi-code',\n codex: 'codex',\n pi: 'pi',\n hermes: 'hermes',\n openclaw: 'openclaw',\n}\n\n/** Resolve the cwd-relative skill dir `resources.skills` refs materialize\n * into on `harness` — via the platform's `skillDirForHarness`. `null` when\n * `harness` isn't bridged (see {@link HARNESS_BRIDGE}) or when the platform\n * itself has no cwd skill primitive for it (e.g. `hermes`, user-dir-only). */\nexport function resolveSkillDir(harness: Harness): string | null {\n const bridged = HARNESS_BRIDGE[harness]\n if (!bridged) return null\n return skillDirForHarness(bridged)\n}\n\n/** Filter `harnesses` down to those with no mounted skill dir (deduped,\n * first-seen order preserved) — the set that must fall back to `inline`\n * delivery, or that a caller should warn about before offering \"mounted\"\n * install UX. */\nexport function unsupportedSkillHarnesses(harnesses: Iterable<Harness>): Harness[] {\n const seen = new Set<Harness>()\n const out: Harness[] = []\n for (const harness of harnesses) {\n if (resolveSkillDir(harness) !== null) continue\n if (seen.has(harness)) continue\n seen.add(harness)\n out.push(harness)\n }\n return out\n}\n\n/** Inputs to {@link composeSkillsForHarness}. */\nexport interface ComposeSkillsForHarnessInput {\n skills: SkillEntry[]\n harness: Harness\n tier?: string\n heading?: string\n /**\n * What to do when `harness` has no native skill-mount directory (see\n * {@link resolveSkillDir}). Default `'throw'`: refuse rather than silently\n * inlining every skill BODY into the system prompt. Pass `'inline'` only as\n * a deliberate, auditable opt-in — the caller has decided every skill must\n * reach this harness even though it will be concatenated into the prompt.\n */\n onNoSkillDir?: 'throw' | 'inline'\n}\n\n/** `KNOWN_HARNESSES` filtered to those the platform materializer can mount\n * skill files onto natively (a non-null {@link resolveSkillDir}). Computed\n * fresh rather than cached — the platform map can grow without a release\n * here. Used only to build the actionable error in\n * {@link composeSkillsForHarness}; not exported because it duplicates\n * {@link unsupportedSkillHarnesses}'s complement and callers should reach\n * for that instead when they need the harness list at runtime. */\nfunction nativeSkillMountHarnesses(): Harness[] {\n return KNOWN_HARNESSES.filter((harness) => resolveSkillDir(harness) !== null)\n}\n\n/**\n * Compose {@link ComposedSkills} for `harness`.\n *\n * `mounted` delivery is the standard path: whenever the platform names a cwd\n * skill dir for `harness`, skills ride the typed `resources.skills` channel\n * and the platform materializer writes each one to that directory as a real\n * file — the agent reads it on demand with its own file tools.\n *\n * `inline` delivery — every skill BODY rendered straight into the system\n * prompt — is a NARROW fallback for harnesses with no native skill dir, never\n * a co-equal mode. An `AgentProfile` is a RECIPE that materializes into a\n * sandbox, not a prompt template: skill bodies belong in a file mount, and\n * `prompt.systemPrompt` should carry only what the agent must obey without a\n * tool call (identity, tool-call contract, safety rules, output format) plus\n * a short index of what's mounted. Concatenating skill bodies into the prompt\n * degrades the model toward empty answers once it grows large enough — one\n * product reproduced a 151,882-byte prompt exactly this way, from an\n * unnoticed inline fallback.\n *\n * Because of that, `onNoSkillDir` defaults to `'throw'`: a harness with no\n * skill dir refuses instead of silently inlining. The thrown error names the\n * harness, the skill count, the byte size that would have been inlined, and\n * the harnesses that DO support native mounting. Pass `onNoSkillDir:\n * 'inline'` only as a deliberate, auditable opt-in when every skill genuinely\n * must reach this harness regardless of prompt cost.\n *\n * The one function a product calls instead of hand-checking `resolveSkillDir`\n * and branching between {@link composeSkills}'s two modes itself.\n */\nexport function composeSkillsForHarness(input: ComposeSkillsForHarnessInput): ComposedSkills {\n const { skills, harness, tier, heading, onNoSkillDir = 'throw' } = input\n const skillDir = resolveSkillDir(harness)\n if (skillDir) return composeSkills({ skills, mode: 'mounted', skillDir, tier, heading })\n if (onNoSkillDir === 'inline') return composeSkills({ skills, mode: 'inline', tier, heading })\n\n const relevant = tier ? skills.filter((s) => s.tier === tier) : skills\n const promptSection = renderInlineSkills({ skills, tier, heading })\n const bytes = new TextEncoder().encode(promptSection).byteLength\n const mountable = nativeSkillMountHarnesses()\n throw new Error(\n `composeSkillsForHarness: \"${harness}\" has no native skill-mount directory. ` +\n `Inlining ${relevant.length} skill(s) (${bytes} bytes) into its system prompt would silently ` +\n `convert mounted skills into prompt text — a 151,882-byte prompt shipped this way once, and ` +\n `oversized prompts degrade toward empty answers. Mount the skills instead: pick a harness with ` +\n `native skill support (${mountable.join(', ')}), or pass onNoSkillDir: 'inline' to opt in ` +\n `explicitly and accept the inlined bytes.`,\n )\n}\n\nexport type { ComposedSkills, SkillEntry } from '../skills/index'\n"],"mappings":";;;;;;;;;AAqBA,SAAS,0BAA0C;AAYnD,IAAM,iBAAsD;AAAA,EAC1D,UAAU;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AAAA,EACV,aAAa;AAAA,EACb,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,UAAU;AACZ;AAMO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,UAAU,eAAe,OAAO;AACtC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,mBAAmB,OAAO;AACnC;AAMO,SAAS,0BAA0B,WAAyC;AACjF,QAAM,OAAO,oBAAI,IAAa;AAC9B,QAAM,MAAiB,CAAC;AACxB,aAAW,WAAW,WAAW;AAC/B,QAAI,gBAAgB,OAAO,MAAM,KAAM;AACvC,QAAI,KAAK,IAAI,OAAO,EAAG;AACvB,SAAK,IAAI,OAAO;AAChB,QAAI,KAAK,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAyBA,SAAS,4BAAuC;AAC9C,SAAO,gBAAgB,OAAO,CAAC,YAAY,gBAAgB,OAAO,MAAM,IAAI;AAC9E;AA+BO,SAAS,wBAAwB,OAAqD;AAC3F,QAAM,EAAE,QAAQ,SAAS,MAAM,SAAS,eAAe,QAAQ,IAAI;AACnE,QAAM,WAAW,gBAAgB,OAAO;AACxC,MAAI,SAAU,QAAO,cAAc,EAAE,QAAQ,MAAM,WAAW,UAAU,MAAM,QAAQ,CAAC;AACvF,MAAI,iBAAiB,SAAU,QAAO,cAAc,EAAE,QAAQ,MAAM,UAAU,MAAM,QAAQ,CAAC;AAE7F,QAAM,WAAW,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI;AAChE,QAAM,gBAAgB,mBAAmB,EAAE,QAAQ,MAAM,QAAQ,CAAC;AAClE,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,aAAa,EAAE;AACtD,QAAM,YAAY,0BAA0B;AAC5C,QAAM,IAAI;AAAA,IACR,6BAA6B,OAAO,mDACtB,SAAS,MAAM,cAAc,KAAK,qQAGrB,UAAU,KAAK,IAAI,CAAC;AAAA,EAEjD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/skills-placement/index.ts"],"sourcesContent":["/**\n * Harness-native skill directory resolution — the one place agent-app binds\n * to the platform's authoritative per-harness skill-dir map.\n *\n * `../skills` renders skill CONTENT (parse, tier-filter, `inline`/`mounted`\n * delivery) but deliberately stops short of naming WHICH cwd path a `mounted`\n * skill lands at on a given harness — that mapping is owned by the platform\n * materializer (`@tangle-network/agent-profile-materialize`'s\n * `skillDirForHarness`), not by app-shell. This subpath exists so no product\n * — and no other agent-app module — ever writes a skill path literal\n * (`~/.claude/skills/...`, `.opencode/skills`, ...) of its own; it bridges\n * agent-app's `Harness` taxonomy onto the platform's `HarnessId` and asks the\n * platform for the answer.\n *\n * Requires the OPTIONAL peer `@tangle-network/agent-profile-materialize`.\n * Products that don't install it simply don't import this subpath — every\n * other agent-app skills surface (`../skills`, `ProfileChannels.skillRefs`)\n * works without it, falling back to `inline` delivery.\n */\n\nimport { KNOWN_HARNESSES, type Harness } from '../harness/index'\nimport { skillDirForHarness, type HarnessId } from '@tangle-network/agent-profile-materialize'\nimport { composeSkills, renderInlineSkills, type ComposedSkills, type SkillEntry } from '../skills/index'\n\n/** agent-app `Harness` -> platform `HarnessId`, identity-mapped for exactly\n * the harnesses the platform map covers. Harnesses absent here (`amp`,\n * `factory-droids`, `forge`, `acp`, `cursor`, `cli-base`) resolve to `null` —\n * {@link composeSkillsForHarness} refuses by default rather than silently\n * delivering `inline`; a caller that genuinely needs `inline` on one of\n * these opts in explicitly (`onNoSkillDir: 'inline'`). `cursor`'s adapter\n * supports `resources.skills` bespokely but isn't in the platform map yet;\n * treating it as unbridged is the safe posture until the map covers it,\n * rather than guessing its cwd skill dir here. */\nconst HARNESS_BRIDGE: Partial<Record<Harness, HarnessId>> = {\n opencode: 'opencode',\n 'claude-code': 'claude-code',\n nanoclaw: 'nanoclaw',\n 'kimi-code': 'kimi-code',\n codex: 'codex',\n pi: 'pi',\n prime: 'prime',\n hermes: 'hermes',\n openclaw: 'openclaw',\n}\n\n/** Resolve the cwd-relative skill dir `resources.skills` refs materialize\n * into on `harness` — via the platform's `skillDirForHarness`. `null` when\n * `harness` isn't bridged (see {@link HARNESS_BRIDGE}) or when the platform\n * itself has no cwd skill primitive for it (e.g. `hermes`, user-dir-only). */\nexport function resolveSkillDir(harness: Harness): string | null {\n const bridged = HARNESS_BRIDGE[harness]\n if (!bridged) return null\n return skillDirForHarness(bridged)\n}\n\n/** Filter `harnesses` down to those with no mounted skill dir (deduped,\n * first-seen order preserved) — the set that must fall back to `inline`\n * delivery, or that a caller should warn about before offering \"mounted\"\n * install UX. */\nexport function unsupportedSkillHarnesses(harnesses: Iterable<Harness>): Harness[] {\n const seen = new Set<Harness>()\n const out: Harness[] = []\n for (const harness of harnesses) {\n if (resolveSkillDir(harness) !== null) continue\n if (seen.has(harness)) continue\n seen.add(harness)\n out.push(harness)\n }\n return out\n}\n\n/** Inputs to {@link composeSkillsForHarness}. */\nexport interface ComposeSkillsForHarnessInput {\n skills: SkillEntry[]\n harness: Harness\n tier?: string\n heading?: string\n /**\n * What to do when `harness` has no native skill-mount directory (see\n * {@link resolveSkillDir}). Default `'throw'`: refuse rather than silently\n * inlining every skill BODY into the system prompt. Pass `'inline'` only as\n * a deliberate, auditable opt-in — the caller has decided every skill must\n * reach this harness even though it will be concatenated into the prompt.\n */\n onNoSkillDir?: 'throw' | 'inline'\n}\n\n/** `KNOWN_HARNESSES` filtered to those the platform materializer can mount\n * skill files onto natively (a non-null {@link resolveSkillDir}). Computed\n * fresh rather than cached — the platform map can grow without a release\n * here. Used only to build the actionable error in\n * {@link composeSkillsForHarness}; not exported because it duplicates\n * {@link unsupportedSkillHarnesses}'s complement and callers should reach\n * for that instead when they need the harness list at runtime. */\nfunction nativeSkillMountHarnesses(): Harness[] {\n return KNOWN_HARNESSES.filter((harness) => resolveSkillDir(harness) !== null)\n}\n\n/**\n * Compose {@link ComposedSkills} for `harness`.\n *\n * `mounted` delivery is the standard path: whenever the platform names a cwd\n * skill dir for `harness`, skills ride the typed `resources.skills` channel\n * and the platform materializer writes each one to that directory as a real\n * file — the agent reads it on demand with its own file tools.\n *\n * `inline` delivery — every skill BODY rendered straight into the system\n * prompt — is a NARROW fallback for harnesses with no native skill dir, never\n * a co-equal mode. An `AgentProfile` is a RECIPE that materializes into a\n * sandbox, not a prompt template: skill bodies belong in a file mount, and\n * `prompt.systemPrompt` should carry only what the agent must obey without a\n * tool call (identity, tool-call contract, safety rules, output format) plus\n * a short index of what's mounted. Concatenating skill bodies into the prompt\n * degrades the model toward empty answers once it grows large enough — one\n * product reproduced a 151,882-byte prompt exactly this way, from an\n * unnoticed inline fallback.\n *\n * Because of that, `onNoSkillDir` defaults to `'throw'`: a harness with no\n * skill dir refuses instead of silently inlining. The thrown error names the\n * harness, the skill count, the byte size that would have been inlined, and\n * the harnesses that DO support native mounting. Pass `onNoSkillDir:\n * 'inline'` only as a deliberate, auditable opt-in when every skill genuinely\n * must reach this harness regardless of prompt cost.\n *\n * The one function a product calls instead of hand-checking `resolveSkillDir`\n * and branching between {@link composeSkills}'s two modes itself.\n */\nexport function composeSkillsForHarness(input: ComposeSkillsForHarnessInput): ComposedSkills {\n const { skills, harness, tier, heading, onNoSkillDir = 'throw' } = input\n const skillDir = resolveSkillDir(harness)\n if (skillDir) return composeSkills({ skills, mode: 'mounted', skillDir, tier, heading })\n if (onNoSkillDir === 'inline') return composeSkills({ skills, mode: 'inline', tier, heading })\n\n const relevant = tier ? skills.filter((s) => s.tier === tier) : skills\n const promptSection = renderInlineSkills({ skills, tier, heading })\n const bytes = new TextEncoder().encode(promptSection).byteLength\n const mountable = nativeSkillMountHarnesses()\n throw new Error(\n `composeSkillsForHarness: \"${harness}\" has no native skill-mount directory. ` +\n `Inlining ${relevant.length} skill(s) (${bytes} bytes) into its system prompt would silently ` +\n `convert mounted skills into prompt text — a 151,882-byte prompt shipped this way once, and ` +\n `oversized prompts degrade toward empty answers. Mount the skills instead: pick a harness with ` +\n `native skill support (${mountable.join(', ')}), or pass onNoSkillDir: 'inline' to opt in ` +\n `explicitly and accept the inlined bytes.`,\n )\n}\n\nexport type { ComposedSkills, SkillEntry } from '../skills/index'\n"],"mappings":";;;;;;;;;AAqBA,SAAS,0BAA0C;AAYnD,IAAM,iBAAsD;AAAA,EAC1D,UAAU;AAAA,EACV,eAAe;AAAA,EACf,UAAU;AAAA,EACV,aAAa;AAAA,EACb,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AACZ;AAMO,SAAS,gBAAgB,SAAiC;AAC/D,QAAM,UAAU,eAAe,OAAO;AACtC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,mBAAmB,OAAO;AACnC;AAMO,SAAS,0BAA0B,WAAyC;AACjF,QAAM,OAAO,oBAAI,IAAa;AAC9B,QAAM,MAAiB,CAAC;AACxB,aAAW,WAAW,WAAW;AAC/B,QAAI,gBAAgB,OAAO,MAAM,KAAM;AACvC,QAAI,KAAK,IAAI,OAAO,EAAG;AACvB,SAAK,IAAI,OAAO;AAChB,QAAI,KAAK,OAAO;AAAA,EAClB;AACA,SAAO;AACT;AAyBA,SAAS,4BAAuC;AAC9C,SAAO,gBAAgB,OAAO,CAAC,YAAY,gBAAgB,OAAO,MAAM,IAAI;AAC9E;AA+BO,SAAS,wBAAwB,OAAqD;AAC3F,QAAM,EAAE,QAAQ,SAAS,MAAM,SAAS,eAAe,QAAQ,IAAI;AACnE,QAAM,WAAW,gBAAgB,OAAO;AACxC,MAAI,SAAU,QAAO,cAAc,EAAE,QAAQ,MAAM,WAAW,UAAU,MAAM,QAAQ,CAAC;AACvF,MAAI,iBAAiB,SAAU,QAAO,cAAc,EAAE,QAAQ,MAAM,UAAU,MAAM,QAAQ,CAAC;AAE7F,QAAM,WAAW,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI;AAChE,QAAM,gBAAgB,mBAAmB,EAAE,QAAQ,MAAM,QAAQ,CAAC;AAClE,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,aAAa,EAAE;AACtD,QAAM,YAAY,0BAA0B;AAC5C,QAAM,IAAI;AAAA,IACR,6BAA6B,OAAO,mDACtB,SAAS,MAAM,cAAc,KAAK,qQAGrB,UAAU,KAAK,IAAI,CAAC;AAAA,EAEjD;AACF;","names":[]}
@@ -522,7 +522,8 @@
522
522
 
523
523
  /* The waiting label. `background-clip: text` over a moving gradient, so the
524
524
  shimmer runs THROUGH the glyphs rather than behind them. Marked essential:
525
- it is the only signal that work is still in flight. */
525
+ it is the only signal that work is still in flight. Its reduced-motion
526
+ answer is at the bottom of this file, with the floor it opts out of. */
526
527
  .agent-shimmer {
527
528
  background: linear-gradient(
528
529
  90deg,
@@ -583,6 +584,13 @@
583
584
  * out by declaring `data-motion="essential"`; the exclusion covers that element
584
585
  * and its subtree, and `:where()` keeps the selector at zero specificity so a
585
586
  * consumer's own `!important` can still win.
587
+ *
588
+ * `essential` is an exemption from the BLANKET rule, not a licence to keep
589
+ * running. It exists because collapsing a meaning-carrying animation to a 1ms
590
+ * flash destroys the meaning, so the element gets to answer for itself — and it
591
+ * then OWES that answer. An element with no end condition owes it most, which
592
+ * is why `.agent-shimmer` states its own below: the sweep stops, and what it
593
+ * was saying is re-stated as a static difference the reader can still see.
586
594
  */
587
595
  @media (prefers-reduced-motion: reduce) {
588
596
  :root {
@@ -603,6 +611,42 @@
603
611
  transition-duration: 1ms !important;
604
612
  }
605
613
 
614
+ /* The waiting label's own answer. It is `data-motion="essential"` because the
615
+ sweep is the ONLY thing separating an agent that is working from one that
616
+ is stuck, so the floor above must not collapse it — but an INFINITE
617
+ animation is exactly what someone asking for reduced motion is asking to be
618
+ rid of, and running it anyway because the element declared itself essential
619
+ is the override this file calls a defect.
620
+
621
+ So the meaning is carried without the motion. The sweep stops and the
622
+ gradient comes off (it exists only to be moved), which puts the glyphs back
623
+ under `color` instead of leaving them transparent windows onto it.
624
+
625
+ The DOTTED RULE is the discriminator, and it is the only one. Measured in
626
+ Chromium under `prefers-reduced-motion: reduce`, light and dark: the live
627
+ label computes `text-decoration: underline dotted` where a settled label
628
+ beside it computes `none`, and the two resolve to the SAME color and the
629
+ same weight. That is by construction — `color: inherit` hands the label
630
+ whatever its context already uses, and the settled label shares that
631
+ context. In `mission-activity`'s lane both tool names read rgb(12,12,21)
632
+ light / rgb(236,236,241) dark; in the chat trace toggle "Thinking · 12s"
633
+ and "Thought for 12s" both read rgb(96,96,99) light / rgb(158,158,160)
634
+ dark, because that button is `text-muted-foreground`. So the reader tells
635
+ in-flight from done by the rule under the word, from a still frame — not by
636
+ tone and not by weight.
637
+
638
+ Not `content: '…'` or any other generated text: this element's text is read
639
+ by a screen reader, and adding pseudo-content changes what is announced. */
640
+ .agent-shimmer {
641
+ animation: none;
642
+ background: none;
643
+ background-clip: border-box;
644
+ -webkit-background-clip: border-box;
645
+ color: inherit;
646
+ text-decoration: underline dotted currentColor;
647
+ text-underline-offset: 3px;
648
+ }
649
+
606
650
  html {
607
651
  scroll-behavior: auto !important;
608
652
  }
@@ -134,11 +134,19 @@ type FieldValues = Record<string, {
134
134
  * intentionally used only when an interaction carries `answers`, never to
135
135
  * guess an answer from the absence of an outstanding sidecar ask. */
136
136
  declare function fieldValuesFromAnswers(fields: ChatInteractionField[], answers: InteractionAnswers | undefined): FieldValues;
137
- /** The submitted value for one field, or null when it has no answer yet. */
138
- declare function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionData[string] | null;
137
+ /** The submitted value for one field, or null when it has no answer yet.
138
+ *
139
+ * Returns `InteractionAnswers[string]`, not the wider `InteractionData[string]`:
140
+ * a card reads its value out of a rendered control, so every branch below
141
+ * yields a plain scalar or string array. `InteractionData` also admits a
142
+ * one-use `secret_handle` reference, which no control here can produce and
143
+ * which `onResolved` must never receive — that path persists into the visible
144
+ * transcript. Declaring the narrow type keeps the handle out by construction
145
+ * rather than by review. */
146
+ declare function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionAnswers[string] | null;
139
147
  /** All required fields answered → the respond payload; else null (not
140
148
  * submittable yet). Optional unanswered fields are omitted. */
141
- declare function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionData | null;
149
+ declare function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionAnswers | null;
142
150
  /** Determine if a status is late answerable by checking if it is expired or cancelled */
143
151
  declare function isLateAnswerableStatus(status: ChatInteractionStatus): boolean;
144
152
  /** Secrets must never leave the sidecar answer channel for the visible chat
@@ -527,7 +535,14 @@ interface QuestionOptionListProps {
527
535
  }
528
536
  /** The radio/checkbox option rows for a select field. Renders a fragment of
529
537
  * option `<label>` rows so a card keeps its own wrapping layout and appends
530
- * its own write-in input. */
538
+ * its own write-in input.
539
+ *
540
+ * The rows arrive as a SEQUENCE (`.agent-arrive` + `--stagger-index`), which
541
+ * is the difference between reading a list and being shown one: the eye is
542
+ * told there are N choices and in what order before it has read any of them.
543
+ * The index is safe to take straight from the map because an option list does
544
+ * not re-sort under a mounted card — a different set of options is a
545
+ * different `key`, and a different ask resets the card wholesale. */
531
546
  declare function QuestionOptionList({ groupName, idPrefix, options, multi, selectedValues, disabled, onToggle, answered, }: QuestionOptionListProps): react.JSX.Element;
532
547
  interface InteractionQuestionCardProps {
533
548
  interaction: ChatInteraction;
@@ -1050,6 +1065,12 @@ interface MissionActivityLaneProps {
1050
1065
  /**
1051
1066
  * Collapsed sub-rows under a mission step — one row per delegated run —
1052
1067
  * expanding to the step's waterfall. Renders nothing for an empty lane.
1068
+ *
1069
+ * A sub-row appears because a delegated run STARTED or FINISHED, which is the
1070
+ * one kind of list change worth choreographing: it arrives, and the group
1071
+ * arrives as a sequence. Keying on `taskId` is what keeps the rest still — the
1072
+ * snapshot re-renders every poll, and a row whose status merely advanced holds
1073
+ * the DOM node it already had.
1053
1074
  */
1054
1075
  declare function MissionActivityLane({ activity, startedAt, nowMs }: MissionActivityLaneProps): react.JSX.Element | null;
1055
1076
  interface AgentActivityPanelProps {
@@ -131,7 +131,7 @@ import {
131
131
  withoutRecordGridCreated,
132
132
  withoutRecordGridRemoved,
133
133
  withoutRecordGridUpdate
134
- } from "../chunk-7HY4LX7O.js";
134
+ } from "../chunk-JEZILUC3.js";
135
135
  import "../chunk-FBVLEGEG.js";
136
136
  import {
137
137
  EvidenceLineageTable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.45.50",
3
+ "version": "0.45.52",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
6
6
  "keywords": [
@@ -518,15 +518,15 @@
518
518
  "@storybook/react": "^10.5.5",
519
519
  "@storybook/react-vite": "^10.5.5",
520
520
  "@tangle-network/agent-docs": "0.2.1",
521
- "@tangle-network/agent-eval": "0.143.0",
522
- "@tangle-network/agent-integrations": "0.53.37",
523
- "@tangle-network/agent-interface": "0.43.0",
524
- "@tangle-network/agent-knowledge": "7.0.8",
525
- "@tangle-network/agent-profile-materialize": "0.10.2",
526
- "@tangle-network/agent-runtime": "0.126.0",
527
- "@tangle-network/brand": "1.3.0",
528
- "@tangle-network/sandbox": "0.17.2",
529
- "@tangle-network/sandbox-ui": "0.99.2",
521
+ "@tangle-network/agent-eval": "0.145.3",
522
+ "@tangle-network/agent-integrations": "0.53.53",
523
+ "@tangle-network/agent-interface": "0.47.0",
524
+ "@tangle-network/agent-knowledge": "7.2.4",
525
+ "@tangle-network/agent-profile-materialize": "0.14.0",
526
+ "@tangle-network/agent-runtime": "0.132.13",
527
+ "@tangle-network/brand": "1.4.0",
528
+ "@tangle-network/sandbox": "0.21.1",
529
+ "@tangle-network/sandbox-ui": "0.100.1",
530
530
  "@tangle-network/ui": "^11.2.2",
531
531
  "@testing-library/dom": "^10.4.1",
532
532
  "@testing-library/react": "^16.3.2",
@@ -570,15 +570,15 @@
570
570
  "@firecrawl/pdf-inspector-wasm": ">=0.1.3",
571
571
  "@huggingface/transformers": ">=3",
572
572
  "@radix-ui/react-dialog": ">=1.1",
573
- "@tangle-network/agent-eval": ">=0.143.0",
573
+ "@tangle-network/agent-eval": ">=0.145.2",
574
574
  "@tangle-network/agent-integrations": ">=0.52.0",
575
- "@tangle-network/agent-interface": ">=0.42.1 <0.44.0",
576
- "@tangle-network/agent-knowledge": ">=6.1.11",
577
- "@tangle-network/agent-profile-materialize": ">=0.9.2",
578
- "@tangle-network/agent-runtime": ">=0.126.0",
579
- "@tangle-network/brand": ">=1.1.0",
580
- "@tangle-network/sandbox": ">=0.15.2",
581
- "@tangle-network/sandbox-ui": ">=0.99.2",
575
+ "@tangle-network/agent-interface": ">=0.47.0 <0.48.0",
576
+ "@tangle-network/agent-knowledge": ">=7.2.4",
577
+ "@tangle-network/agent-profile-materialize": ">=0.14.0",
578
+ "@tangle-network/agent-runtime": ">=0.132.13",
579
+ "@tangle-network/brand": ">=1.4.0",
580
+ "@tangle-network/sandbox": ">=0.21.1",
581
+ "@tangle-network/sandbox-ui": ">=0.100.1",
582
582
  "@tangle-network/ui": ">=11.2.2",
583
583
  "@xyflow/react": ">=12.0.0",
584
584
  "better-auth": ">=1.6.16",