@tangle-network/agent-provider-tangle 1.2.1 → 1.2.2
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/README.md
CHANGED
|
@@ -29,6 +29,37 @@ const provider = createTangleProvider({
|
|
|
29
29
|
`Sandbox` requires an explicit `baseUrl`.
|
|
30
30
|
Set `TANGLE_SANDBOX_URL` to use another deployment.
|
|
31
31
|
|
|
32
|
+
## Named model credentials at creation
|
|
33
|
+
|
|
34
|
+
`modelCredentials` sends a stored credential reference and explicit endpoint before Sandbox provisions the backend.
|
|
35
|
+
This uses Sandbox's existing caller-owned model credential path instead of requesting a managed Router credential.
|
|
36
|
+
The provider does not create, grant, copy, or renew secrets.
|
|
37
|
+
Each create must list the named secret explicitly.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
const provider = createTangleProvider({
|
|
41
|
+
client,
|
|
42
|
+
modelCredentials: {
|
|
43
|
+
apiKeyEnv: "RESEARCH_ROUTER_KEY",
|
|
44
|
+
baseUrl: "https://router.tangle.tools/v1",
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
const environment = await provider.create({
|
|
48
|
+
profile: researchProfile,
|
|
49
|
+
secrets: ["RESEARCH_ROUTER_KEY"],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The configuration accepts only the reference and endpoint, not credential values or model selection.
|
|
54
|
+
The authored profile, selected backend, runtime attachments, and create identity remain unchanged.
|
|
55
|
+
The provider snapshots this configuration when constructed and refuses combining it with `mapCreateInput`.
|
|
56
|
+
Omitting it preserves managed creation.
|
|
57
|
+
The endpoint must use HTTP or HTTPS without credentials, query parameters, or fragments.
|
|
58
|
+
|
|
59
|
+
Credential-only turns retain the created SDK handle's endpoint and attachments.
|
|
60
|
+
Reconstructed handles leave omitted model settings to Sandbox's durable backend configuration.
|
|
61
|
+
Keep the stored secret available and valid throughout execution and recovery.
|
|
62
|
+
|
|
32
63
|
## `create()` returns a ready environment
|
|
33
64
|
|
|
34
65
|
`provider.create()` does not return until the sandbox reports `running`.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type BackendType, type CreateSandboxOptions } from "@tangle-network/sandbox";
|
|
2
2
|
import type { CreateAgentEnvironmentInput, WorkspaceRequest } from "@tangle-network/agent-interface/environment-provider";
|
|
3
|
-
|
|
3
|
+
import type { TangleProviderOptions } from "./tangle-types.js";
|
|
4
|
+
export declare function captureModelCredentials(value: TangleProviderOptions["modelCredentials"]): TangleProviderOptions["modelCredentials"];
|
|
5
|
+
export declare function sandboxOptionsFromCreateInput(input: CreateAgentEnvironmentInput, defaultBackend?: BackendType, parsedWorkspace?: WorkspaceRequest, modelCredentials?: TangleProviderOptions["modelCredentials"]): CreateSandboxOptions;
|
|
4
6
|
/** Reject value-bearing secret maps before any custom mapper can drop them. */
|
|
5
7
|
export declare function assertNoInlineSecretValues(input: CreateAgentEnvironmentInput, parsedWorkspace?: WorkspaceRequest): void;
|
|
6
8
|
/** A custom mapper must not smuggle a value map into the Sandbox request. */
|
|
@@ -3,13 +3,44 @@ import { AgentEnvironmentEgressPolicySchema, WorkspaceRequestSchema, workspaceCw
|
|
|
3
3
|
import { assertBoundedJson, boundedIdentifier, boundedString, MAX_ARRAY_LENGTH, MAX_MAP_ENTRIES, } from "./tangle-contract-safety.js";
|
|
4
4
|
import { sandboxResourcesFromResourceRequest } from "./tangle-resources.js";
|
|
5
5
|
import { tangleRuntimeAttachments } from "./tangle-runtime-attachments.js";
|
|
6
|
-
export function
|
|
6
|
+
export function captureModelCredentials(value) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
assertBoundedJson(value);
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value) ||
|
|
11
|
+
Object.keys(value).some((key) => key !== "apiKeyEnv" && key !== "baseUrl")) {
|
|
12
|
+
throw new Error("Tangle modelCredentials accepts only apiKeyEnv and baseUrl");
|
|
13
|
+
}
|
|
14
|
+
const { apiKeyEnv, baseUrl } = value;
|
|
15
|
+
if (typeof apiKeyEnv !== "string" || apiKeyEnv.length > 128 ||
|
|
16
|
+
!/^[A-Z_][A-Z0-9_]*$/.test(apiKeyEnv)) {
|
|
17
|
+
throw new Error("Tangle modelCredentials apiKeyEnv must be a stored secret name");
|
|
18
|
+
}
|
|
19
|
+
let url;
|
|
20
|
+
try {
|
|
21
|
+
url = new URL(baseUrl);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error("Tangle modelCredentials baseUrl must be an HTTP(S) endpoint");
|
|
25
|
+
}
|
|
26
|
+
if (typeof baseUrl !== "string" || baseUrl.length > 2048 || /\s/.test(baseUrl) ||
|
|
27
|
+
!["http:", "https:"].includes(url.protocol) ||
|
|
28
|
+
url.username || url.password || url.search || url.hash) {
|
|
29
|
+
throw new Error("Tangle modelCredentials baseUrl must be an HTTP(S) endpoint without credentials, query, or fragment");
|
|
30
|
+
}
|
|
31
|
+
return Object.freeze({ apiKeyEnv, baseUrl });
|
|
32
|
+
}
|
|
33
|
+
export function sandboxOptionsFromCreateInput(input, defaultBackend, parsedWorkspace, modelCredentials) {
|
|
7
34
|
const workspace = assertCreateInputShape(input, parsedWorkspace) ?? {};
|
|
8
35
|
const profile = inlineAgentProfile(input.profile);
|
|
9
36
|
const backend = input.backend === undefined
|
|
10
37
|
? parseBackendType(defaultBackend ?? profile.harness ?? "opencode")
|
|
11
38
|
: input.backend;
|
|
12
39
|
assertNoInlineSecretValues(input, workspace);
|
|
40
|
+
if (modelCredentials !== undefined &&
|
|
41
|
+
(!Array.isArray(input.secrets) || !input.secrets.includes(modelCredentials.apiKeyEnv))) {
|
|
42
|
+
throw new Error("Tangle modelCredentials apiKeyEnv must be explicitly listed in create secrets");
|
|
43
|
+
}
|
|
13
44
|
if (input.providerOptions && Object.keys(input.providerOptions).length > 0) {
|
|
14
45
|
throw new Error("Tangle create providerOptions are not supported");
|
|
15
46
|
}
|
|
@@ -71,6 +102,7 @@ export function sandboxOptionsFromCreateInput(input, defaultBackend, parsedWorks
|
|
|
71
102
|
...(base.backend ?? {}),
|
|
72
103
|
type: backend,
|
|
73
104
|
profile,
|
|
105
|
+
...(modelCredentials === undefined ? {} : { model: modelCredentials }),
|
|
74
106
|
...(input.runtimeAttachments === undefined ? {} : {
|
|
75
107
|
runtimeAttachments: tangleRuntimeAttachments(input.runtimeAttachments, profile),
|
|
76
108
|
}),
|
package/dist/tangle-provider.js
CHANGED
|
@@ -4,12 +4,17 @@ import { createTangleExactProcessProvider, } from "./exact-process.js";
|
|
|
4
4
|
import { capabilitiesForClient, defaultTangleSandboxCapabilities, narrowTangleCapabilitiesToBackend, } from "./tangle-capabilities.js";
|
|
5
5
|
import { sandboxInstanceAsEnvironment } from "./tangle-environment.js";
|
|
6
6
|
import { confidentialVerifierOption, createTangleWorkspaceBranching, } from "./tangle-workspace-branching.js";
|
|
7
|
-
import { assertCreateInputShape, assertMappedCreateOptions, assertMappedSecretNames, assertNoInlineSecretValues, sandboxOptionsFromCreateInput } from "./tangle-create-options.js";
|
|
7
|
+
import { assertCreateInputShape, assertMappedCreateOptions, assertMappedSecretNames, assertNoInlineSecretValues, captureModelCredentials, sandboxOptionsFromCreateInput } from "./tangle-create-options.js";
|
|
8
8
|
import { statusFromUnknown } from "./tangle-environment-values.js";
|
|
9
9
|
import { awaitSandboxRunning, DEFAULT_TANGLE_READY_TIMEOUT_MS, } from "./tangle-readiness.js";
|
|
10
10
|
import { requestedResourceProfile } from "./tangle-resources.js";
|
|
11
11
|
import { assertBoundedJson, attachCleanupHandle, awaitWithSignal, boundedIdentifier, boundedString, MAX_LIST_RESULTS, SANDBOX_LIST_PAGE_SIZE, } from "./tangle-contract-safety.js";
|
|
12
12
|
export function createTangleProvider(options) {
|
|
13
|
+
const modelCredentials = captureModelCredentials(options.modelCredentials);
|
|
14
|
+
const mapCreateInput = options.mapCreateInput;
|
|
15
|
+
if (modelCredentials !== undefined && mapCreateInput !== undefined) {
|
|
16
|
+
throw new Error("Tangle modelCredentials cannot be combined with mapCreateInput");
|
|
17
|
+
}
|
|
13
18
|
const providerName = options.name ?? "tangle-sandbox";
|
|
14
19
|
boundedIdentifier(providerName, "Tangle provider name");
|
|
15
20
|
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_TANGLE_READY_TIMEOUT_MS;
|
|
@@ -57,8 +62,8 @@ export function createTangleProvider(options) {
|
|
|
57
62
|
if (input.providerOptions && Object.keys(input.providerOptions).length > 0) {
|
|
58
63
|
throw new Error("Tangle create providerOptions are not supported");
|
|
59
64
|
}
|
|
60
|
-
const mappedOptions =
|
|
61
|
-
sandboxOptionsFromCreateInput(input, options.defaultBackend, parsedWorkspace);
|
|
65
|
+
const mappedOptions = mapCreateInput?.(input) ??
|
|
66
|
+
sandboxOptionsFromCreateInput(input, options.defaultBackend, parsedWorkspace, modelCredentials);
|
|
62
67
|
assertMappedCreateOptions(mappedOptions);
|
|
63
68
|
const createOptions = deepFreeze(structuredClone(mappedOptions));
|
|
64
69
|
if (input.idempotencyKey !== undefined &&
|
package/dist/tangle-types.d.ts
CHANGED
|
@@ -586,6 +586,15 @@ export interface TangleProviderOptions {
|
|
|
586
586
|
name?: string;
|
|
587
587
|
/** Overrides an inline profile's harness when create() names no backend. */
|
|
588
588
|
defaultBackend?: BackendType;
|
|
589
|
+
/**
|
|
590
|
+
* Stored model credential and endpoint used by the default create mapping.
|
|
591
|
+
* Each create must explicitly list apiKeyEnv in its secrets.
|
|
592
|
+
* Cannot be combined with mapCreateInput; never accepts credential values.
|
|
593
|
+
*/
|
|
594
|
+
modelCredentials?: {
|
|
595
|
+
apiKeyEnv: string;
|
|
596
|
+
baseUrl: string;
|
|
597
|
+
};
|
|
589
598
|
capabilities?: AgentEnvironmentCapabilities | (() => AgentEnvironmentCapabilities | Promise<AgentEnvironmentCapabilities>);
|
|
590
599
|
validateProfile?: AgentEnvironmentProvider["validateProfile"];
|
|
591
600
|
/** Replaces the default mapping, including its backend and profile selection. */
|