@stigmer/runner 3.12.3 → 3.12.5
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/.build-fingerprint +1 -1
- package/dist/activities/call-http.js +12 -0
- package/dist/activities/call-http.js.map +1 -1
- package/dist/activities/call-llm.d.ts +18 -0
- package/dist/activities/call-llm.js +56 -2
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/execute-cursor/agent-session-cache.d.ts +72 -0
- package/dist/activities/execute-cursor/agent-session-cache.js +186 -0
- package/dist/activities/execute-cursor/agent-session-cache.js.map +1 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.d.ts +45 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.js +76 -0
- package/dist/activities/execute-cursor/cursor-mcp-config.js.map +1 -0
- package/dist/activities/execute-cursor/index.d.ts +10 -1
- package/dist/activities/execute-cursor/index.js +108 -57
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +13 -2
- package/dist/activities/execute-cursor/prompt-builder.js +11 -0
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/service-tier.d.ts +5 -15
- package/dist/activities/execute-cursor/service-tier.js +5 -21
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- package/dist/activities/execute-cursor/session-lifecycle.d.ts +1 -1
- package/dist/activities/execute-cursor/skill-resolver.d.ts +2 -21
- package/dist/activities/execute-cursor/skill-resolver.js +5 -108
- package/dist/activities/execute-cursor/skill-resolver.js.map +1 -1
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +9 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +2 -1
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +18 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +20 -3
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.js +57 -5
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +9 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js +10 -0
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.js +31 -16
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/shell-env.d.ts +8 -5
- package/dist/activities/execute-deep-agent/shell-env.js +10 -7
- package/dist/activities/execute-deep-agent/shell-env.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.js +17 -11
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -3
- package/dist/config.js +10 -5
- package/dist/config.js.map +1 -1
- package/dist/encryption/config.js +7 -2
- package/dist/encryption/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.js +12 -6
- package/dist/main.js.map +1 -1
- package/dist/middleware/path-normalization.d.ts +49 -49
- package/dist/middleware/path-normalization.js +73 -84
- package/dist/middleware/path-normalization.js.map +1 -1
- package/dist/middleware/types.d.ts +3 -3
- package/dist/payload-codecs.js +2 -1
- package/dist/payload-codecs.js.map +1 -1
- package/dist/runner-manager.d.ts +1 -1
- package/dist/runner-manager.js +42 -22
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +30 -6
- package/dist/runner.js.map +1 -1
- package/dist/shared/args-preview.d.ts +8 -0
- package/dist/shared/args-preview.js +14 -3
- package/dist/shared/args-preview.js.map +1 -1
- package/dist/shared/artifact-storage.d.ts +11 -1
- package/dist/shared/artifact-storage.js +10 -1
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/declared-preferences.d.ts +47 -0
- package/dist/shared/declared-preferences.js +64 -0
- package/dist/shared/declared-preferences.js.map +1 -0
- package/dist/shared/fingerprint-secret.d.ts +3 -2
- package/dist/shared/fingerprint-secret.js +5 -3
- package/dist/shared/fingerprint-secret.js.map +1 -1
- package/dist/shared/llm-backend.js +8 -1
- package/dist/shared/llm-backend.js.map +1 -1
- package/dist/shared/mcp-enabled-tools.d.ts +2 -3
- package/dist/shared/mcp-enabled-tools.js +2 -3
- package/dist/shared/mcp-enabled-tools.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +20 -15
- package/dist/shared/mcp-resolver.js +11 -12
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/model-client.d.ts +15 -0
- package/dist/shared/model-client.js +57 -13
- package/dist/shared/model-client.js.map +1 -1
- package/dist/shared/placeholder-resolver.d.ts +9 -2
- package/dist/shared/placeholder-resolver.js +9 -2
- package/dist/shared/placeholder-resolver.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +26 -46
- package/dist/shared/plan-mode-permissions.js +27 -55
- package/dist/shared/plan-mode-permissions.js.map +1 -1
- package/dist/shared/registry-endpoint.d.ts +5 -0
- package/dist/shared/registry-endpoint.js +7 -1
- package/dist/shared/registry-endpoint.js.map +1 -1
- package/dist/shared/runner-credential-keys.d.ts +26 -1
- package/dist/shared/runner-credential-keys.js +34 -1
- package/dist/shared/runner-credential-keys.js.map +1 -1
- package/dist/shared/runner-credential-store.d.ts +77 -0
- package/dist/shared/runner-credential-store.js +111 -0
- package/dist/shared/runner-credential-store.js.map +1 -0
- package/dist/shared/service-tier.d.ts +55 -0
- package/dist/shared/service-tier.js +67 -0
- package/dist/shared/service-tier.js.map +1 -0
- package/dist/shared/skill-mount.d.ts +89 -0
- package/dist/shared/skill-mount.js +142 -0
- package/dist/shared/skill-mount.js.map +1 -0
- package/dist/shared/skill-writer.d.ts +28 -26
- package/dist/shared/skill-writer.js +79 -102
- package/dist/shared/skill-writer.js.map +1 -1
- package/dist/shared/worker-shutdown.d.ts +67 -0
- package/dist/shared/worker-shutdown.js +79 -0
- package/dist/shared/worker-shutdown.js.map +1 -0
- package/dist/shared/workspace/types.d.ts +3 -2
- package/dist/shared/zip-extract.d.ts +10 -3
- package/dist/shared/zip-extract.js +10 -3
- package/dist/shared/zip-extract.js.map +1 -1
- package/dist/workflow-engine/loader.js +38 -1
- package/dist/workflow-engine/loader.js.map +1 -1
- package/dist/workflow-engine/tasks/call-function.d.ts +14 -0
- package/dist/workflow-engine/tasks/call-function.js +49 -5
- package/dist/workflow-engine/tasks/call-function.js.map +1 -1
- package/dist/workflow-engine/tasks/human-input.js +23 -2
- package/dist/workflow-engine/tasks/human-input.js.map +1 -1
- package/dist/workflow-engine/types.d.ts +6 -0
- package/dist/workflow-engine/types.js.map +1 -1
- package/dist/workflows/engine-core.js +36 -8
- package/dist/workflows/engine-core.js.map +1 -1
- package/package.json +15 -11
- package/src/activities/__tests__/call-http.test.ts +36 -0
- package/src/activities/__tests__/call-llm.test.ts +77 -0
- package/src/activities/call-http.ts +17 -0
- package/src/activities/call-llm.ts +78 -2
- package/src/activities/execute-cursor/__tests__/agent-session-cache.test.ts +220 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +50 -0
- package/src/activities/execute-cursor/__tests__/cursor-mcp-config.test.ts +111 -0
- package/src/activities/execute-cursor/__tests__/service-tier.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/skill-resolver.test.ts +3 -94
- package/src/activities/execute-cursor/agent-session-cache.ts +229 -0
- package/src/activities/execute-cursor/cursor-mcp-config.ts +107 -0
- package/src/activities/execute-cursor/index.ts +129 -55
- package/src/activities/execute-cursor/prompt-builder.ts +29 -2
- package/src/activities/execute-cursor/service-tier.ts +5 -29
- package/src/activities/execute-cursor/session-lifecycle.ts +1 -1
- package/src/activities/execute-cursor/skill-resolver.ts +10 -137
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +15 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +75 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/index.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +42 -29
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +52 -0
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +1 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +11 -8
- package/src/activities/execute-deep-agent/approval-file-change.ts +11 -1
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +20 -3
- package/src/activities/execute-deep-agent/index.ts +57 -5
- package/src/activities/execute-deep-agent/prompt-builder.ts +22 -0
- package/src/activities/execute-deep-agent/setup.ts +36 -19
- package/src/activities/execute-deep-agent/shell-env.ts +10 -7
- package/src/activities/execute-deep-agent/subagent-transformer.ts +19 -12
- package/src/activities/execute-deep-agent/subagent-wiring.ts +3 -3
- package/src/config.ts +10 -5
- package/src/encryption/config.ts +8 -2
- package/src/index.ts +1 -1
- package/src/main.ts +16 -6
- package/src/middleware/__tests__/path-normalization.test.ts +32 -32
- package/src/middleware/path-normalization.ts +78 -90
- package/src/middleware/types.ts +3 -3
- package/src/payload-codecs.ts +2 -1
- package/src/runner-manager.ts +55 -22
- package/src/runner.ts +39 -6
- package/src/shared/__tests__/artifact-storage.test.ts +28 -0
- package/src/shared/__tests__/bedrock-seam.test.ts +5 -5
- package/src/shared/__tests__/declared-preferences.test.ts +109 -0
- package/src/shared/__tests__/foundry-seam.test.ts +5 -5
- package/src/shared/__tests__/model-client.test.ts +99 -0
- package/src/shared/__tests__/plan-mode-permissions.test.ts +15 -44
- package/src/shared/__tests__/runner-credential-store.test.ts +155 -0
- package/src/shared/__tests__/skill-mount.test.ts +238 -0
- package/src/shared/__tests__/skill-writer.test.ts +151 -128
- package/src/shared/__tests__/vertex-adapter.test.ts +30 -3
- package/src/shared/__tests__/vertex-seam.test.ts +5 -5
- package/src/shared/__tests__/worker-shutdown.test.ts +101 -0
- package/src/shared/__tests__/zip-extract.test.ts +46 -11
- package/src/shared/args-preview.ts +17 -3
- package/src/shared/artifact-storage.ts +20 -1
- package/src/shared/declared-preferences.ts +84 -0
- package/src/shared/fingerprint-secret.ts +5 -3
- package/src/shared/llm-backend.ts +7 -1
- package/src/shared/mcp-enabled-tools.ts +2 -3
- package/src/shared/mcp-resolver.ts +20 -20
- package/src/shared/model-client.ts +76 -13
- package/src/shared/placeholder-resolver.ts +9 -2
- package/src/shared/plan-mode-permissions.ts +27 -58
- package/src/shared/registry-endpoint.ts +9 -1
- package/src/shared/runner-credential-keys.ts +36 -1
- package/src/shared/runner-credential-store.ts +115 -0
- package/src/shared/service-tier.ts +78 -0
- package/src/shared/skill-mount.ts +179 -0
- package/src/shared/skill-writer.ts +96 -130
- package/src/shared/worker-shutdown.ts +99 -0
- package/src/shared/workspace/types.ts +3 -2
- package/src/shared/zip-extract.ts +14 -7
- package/src/workflow-engine/__tests__/golden-execution.test.ts +20 -1
- package/src/workflow-engine/__tests__/loader.test.ts +51 -0
- package/src/workflow-engine/__tests__/tasks/call-function.test.ts +105 -0
- package/src/workflow-engine/__tests__/tasks/human-input.test.ts +117 -0
- package/src/workflow-engine/loader.ts +46 -1
- package/src/workflow-engine/tasks/call-function.ts +74 -13
- package/src/workflow-engine/tasks/human-input.ts +33 -5
- package/src/workflow-engine/types.ts +6 -0
- package/src/workflows/engine-core.ts +39 -8
- package/dist/activities/execute-cursor/connect-backfill.d.ts +0 -19
- package/dist/activities/execute-cursor/connect-backfill.js +0 -27
- package/dist/activities/execute-cursor/connect-backfill.js.map +0 -1
- package/dist/activities/execute-cursor/mcp-resolver.d.ts +0 -120
- package/dist/activities/execute-cursor/mcp-resolver.js +0 -194
- package/dist/activities/execute-cursor/mcp-resolver.js.map +0 -1
- package/dist/activities/execute-cursor/placeholder-resolver.d.ts +0 -34
- package/dist/activities/execute-cursor/placeholder-resolver.js +0 -82
- package/dist/activities/execute-cursor/placeholder-resolver.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/mcp-resolver.test.ts +0 -125
- package/src/activities/execute-cursor/connect-backfill.ts +0 -52
- package/src/activities/execute-cursor/mcp-resolver.ts +0 -324
- package/src/activities/execute-cursor/placeholder-resolver.ts +0 -109
|
@@ -618,6 +618,22 @@ describe("createArtifactStorage", () => {
|
|
|
618
618
|
};
|
|
619
619
|
expect(() => createArtifactStorage(cfg)).toThrow("STIGMER_TOKEN");
|
|
620
620
|
});
|
|
621
|
+
|
|
622
|
+
it("throws for the deliberate 'none' posture — resolveUsable degrades it, claimcheck fail-hards", async () => {
|
|
623
|
+
const cfg: ArtifactStorageConfig = {
|
|
624
|
+
type: "none",
|
|
625
|
+
localPath: "/tmp/artifacts",
|
|
626
|
+
localServeUrl: "http://localhost:7235",
|
|
627
|
+
proxyEndpoint: null,
|
|
628
|
+
proxyAuthToken: null,
|
|
629
|
+
};
|
|
630
|
+
expect(() => createArtifactStorage(cfg)).toThrow(/deliberately disabled/i);
|
|
631
|
+
// The capture/offload resolver treats it as the first-class absent store:
|
|
632
|
+
// deny-gate mode, not a crash.
|
|
633
|
+
await expect(
|
|
634
|
+
resolveUsableArtifactStorage(cfg, { executionId: "exec-none" }),
|
|
635
|
+
).resolves.toBeUndefined();
|
|
636
|
+
});
|
|
621
637
|
});
|
|
622
638
|
|
|
623
639
|
describe("loadArtifactStorageConfig", () => {
|
|
@@ -680,6 +696,18 @@ describe("loadArtifactStorageConfig", () => {
|
|
|
680
696
|
expect(cfg.proxyAuthToken).toBe("tok");
|
|
681
697
|
});
|
|
682
698
|
|
|
699
|
+
it("honors ARTIFACT_STORAGE_TYPE=none even when a proxy endpoint is configured", () => {
|
|
700
|
+
// "none" is a deliberate operator/e2e posture — it must beat the
|
|
701
|
+
// storage-follows-transport default exactly like the other overrides.
|
|
702
|
+
process.env.ARTIFACT_STORAGE_TYPE = "none";
|
|
703
|
+
const cfg = loadArtifactStorageConfig({
|
|
704
|
+
...baseConfig,
|
|
705
|
+
proxyEndpoint: "https://proxy.example.com",
|
|
706
|
+
stigmerToken: "tok",
|
|
707
|
+
});
|
|
708
|
+
expect(cfg.type).toBe("none");
|
|
709
|
+
});
|
|
710
|
+
|
|
683
711
|
it("respects ARTIFACT_STORAGE_TYPE override", () => {
|
|
684
712
|
process.env.ARTIFACT_STORAGE_TYPE = "proxy";
|
|
685
713
|
const cfg = loadArtifactStorageConfig({
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* Characterization test for the ChatAnthropic `createClient` ->
|
|
3
3
|
* AnthropicBedrock seam — the integration the T04 bedrock backend adapter
|
|
4
4
|
* is built on. Sibling of vertex-seam.test.ts; same rules: this pins REAL
|
|
5
|
-
* cross-package behavior (`@langchain/anthropic`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* of in production. See scripts/check-langchain-deps.sh for
|
|
9
|
-
*
|
|
5
|
+
* cross-package behavior (`@langchain/anthropic` driving
|
|
6
|
+
* `@anthropic-ai/bedrock-sdk`, both resolving the single override-pinned
|
|
7
|
+
* @anthropic-ai/sdk copy), so a future bump of either side fails here in
|
|
8
|
+
* CI instead of in production. See scripts/check-langchain-deps.sh for
|
|
9
|
+
* the override rationale and the single-copy invariant it guards.
|
|
10
10
|
*
|
|
11
11
|
* Determinism: zero live credentials, zero network. SigV4 signing runs for
|
|
12
12
|
* real — it is pure HMAC over static test keys passed through the SDK's
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the declared-preferences module (stigmer/stigmer#293,
|
|
3
|
+
* DD-002). Like conversation-catchup there is no string key to mirror-guard —
|
|
4
|
+
* the value rides the typed `AgentExecutionSpec.declared_preferences` proto
|
|
5
|
+
* field, so codegen enforces the cross-repo contract. What IS pinned here:
|
|
6
|
+
* the per-scope blank-is-absent read semantics (the server stamps the field
|
|
7
|
+
* on EVERY eligible create, usually with blank scopes), the per-scope
|
|
8
|
+
* attribution, and the framing's behavioral contract.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, it, expect } from "vitest";
|
|
12
|
+
import { create } from "@bufbuild/protobuf";
|
|
13
|
+
import { DeclaredPreferencesSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/spec_pb";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
formatDeclaredPreferencesText,
|
|
17
|
+
readDeclaredPreferences,
|
|
18
|
+
} from "../declared-preferences.js";
|
|
19
|
+
|
|
20
|
+
const ORG_CONTEXT = "We deploy to us-east-1. All costs in USD.";
|
|
21
|
+
const USER_CONTEXT = "Keep answers terse. Prefer Go examples.";
|
|
22
|
+
|
|
23
|
+
describe("readDeclaredPreferences", () => {
|
|
24
|
+
it("reads both scopes when both are set", () => {
|
|
25
|
+
const preferences = create(DeclaredPreferencesSchema, {
|
|
26
|
+
orgContext: ORG_CONTEXT,
|
|
27
|
+
userContext: USER_CONTEXT,
|
|
28
|
+
});
|
|
29
|
+
expect(readDeclaredPreferences(preferences)).toEqual({
|
|
30
|
+
orgContext: ORG_CONTEXT,
|
|
31
|
+
userContext: USER_CONTEXT,
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("answers undefined when the field is absent (pre-#293 executions)", () => {
|
|
36
|
+
expect(readDeclaredPreferences(undefined)).toBeUndefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("answers undefined when both scopes are blank — the server stamps the field on every eligible create", () => {
|
|
40
|
+
const preferences = create(DeclaredPreferencesSchema, {
|
|
41
|
+
orgContext: " ",
|
|
42
|
+
userContext: "",
|
|
43
|
+
});
|
|
44
|
+
expect(readDeclaredPreferences(preferences)).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("blank-is-absent applies per scope: an org-only snapshot carries no user scope", () => {
|
|
48
|
+
const preferences = create(DeclaredPreferencesSchema, {
|
|
49
|
+
orgContext: ORG_CONTEXT,
|
|
50
|
+
userContext: " ",
|
|
51
|
+
});
|
|
52
|
+
expect(readDeclaredPreferences(preferences)).toEqual({
|
|
53
|
+
orgContext: ORG_CONTEXT,
|
|
54
|
+
userContext: undefined,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("trims surrounding whitespace from each scope", () => {
|
|
59
|
+
const preferences = create(DeclaredPreferencesSchema, {
|
|
60
|
+
orgContext: " org facts ",
|
|
61
|
+
userContext: "\nuser facts\n",
|
|
62
|
+
});
|
|
63
|
+
expect(readDeclaredPreferences(preferences)).toEqual({
|
|
64
|
+
orgContext: "org facts",
|
|
65
|
+
userContext: "user facts",
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("formatDeclaredPreferencesText", () => {
|
|
71
|
+
it("frames the preferences as already-known, non-announced background", () => {
|
|
72
|
+
const framed = formatDeclaredPreferencesText({
|
|
73
|
+
orgContext: ORG_CONTEXT,
|
|
74
|
+
userContext: USER_CONTEXT,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(framed).toContain("Standing preferences declared by");
|
|
78
|
+
expect(framed).toContain("Do not repeat them back");
|
|
79
|
+
expect(framed).toContain("not instructions that override your task");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("attributes each scope explicitly, organization first, user last", () => {
|
|
83
|
+
const framed = formatDeclaredPreferencesText({
|
|
84
|
+
orgContext: ORG_CONTEXT,
|
|
85
|
+
userContext: USER_CONTEXT,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const org = framed.indexOf("Declared by the organization:");
|
|
89
|
+
const user = framed.indexOf("Declared by the user:");
|
|
90
|
+
expect(org).toBeGreaterThan(-1);
|
|
91
|
+
expect(user).toBeGreaterThan(org);
|
|
92
|
+
expect(framed).toContain(ORG_CONTEXT);
|
|
93
|
+
expect(framed.endsWith(USER_CONTEXT)).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("renders only the org subsection for an org-only snapshot", () => {
|
|
97
|
+
const framed = formatDeclaredPreferencesText({ orgContext: ORG_CONTEXT });
|
|
98
|
+
|
|
99
|
+
expect(framed).toContain("Declared by the organization:");
|
|
100
|
+
expect(framed).not.toContain("Declared by the user:");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("renders only the user subsection for a user-only snapshot", () => {
|
|
104
|
+
const framed = formatDeclaredPreferencesText({ userContext: USER_CONTEXT });
|
|
105
|
+
|
|
106
|
+
expect(framed).not.toContain("Declared by the organization:");
|
|
107
|
+
expect(framed).toContain("Declared by the user:");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* AnthropicFoundry seam — the integration the T05 foundry backend adapter
|
|
4
4
|
* is built on. Sibling of vertex-seam.test.ts and bedrock-seam.test.ts;
|
|
5
5
|
* same rules: this pins REAL cross-package behavior (`@langchain/anthropic`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* fails here in CI instead of in production. See
|
|
9
|
-
* scripts/check-langchain-deps.sh for
|
|
10
|
-
*
|
|
6
|
+
* driving `@anthropic-ai/foundry-sdk`, both resolving the single
|
|
7
|
+
* override-pinned @anthropic-ai/sdk copy), so a future bump of either
|
|
8
|
+
* side fails here in CI instead of in production. See
|
|
9
|
+
* scripts/check-langchain-deps.sh for the override rationale and the
|
|
10
|
+
* single-copy invariant it guards.
|
|
11
11
|
*
|
|
12
12
|
* Determinism: zero live credentials, zero network. API-key auth is a
|
|
13
13
|
* static test key; Entra ID auth is a fake `azureADTokenProvider` (the
|
|
@@ -23,6 +23,7 @@ vi.mock("@langchain/openai", () => ({
|
|
|
23
23
|
|
|
24
24
|
import { buildChatModel } from "../model-client.js";
|
|
25
25
|
import { _resetRegistryCache } from "../model-registry.js";
|
|
26
|
+
import { ServiceTier } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
26
27
|
|
|
27
28
|
interface MockModel {
|
|
28
29
|
id: string;
|
|
@@ -306,6 +307,18 @@ describe("buildChatModel", () => {
|
|
|
306
307
|
expect(lastAnthropicArgs().apiKey).toBe("");
|
|
307
308
|
});
|
|
308
309
|
|
|
310
|
+
it("does NOT send service_tier on a backend adapter — tiers are Anthropic-first-party billing, and Vertex/Bedrock/Foundry have no tier dimension", async () => {
|
|
311
|
+
mockRegistryResponse([
|
|
312
|
+
{ id: "claude-sonnet-4.6", apiModelId: "claude-sonnet-4-6", provider: "anthropic" },
|
|
313
|
+
]);
|
|
314
|
+
|
|
315
|
+
await buildChatModel({ modelName: "claude-sonnet-4.6", serviceTier: ServiceTier.STANDARD });
|
|
316
|
+
|
|
317
|
+
const args = lastAnthropicArgs();
|
|
318
|
+
expect(typeof args.createClient).toBe("function");
|
|
319
|
+
expect(args).not.toHaveProperty("invocationKwargs");
|
|
320
|
+
});
|
|
321
|
+
|
|
309
322
|
it("yields to the proxy: a proxied call never consults the backend var", async () => {
|
|
310
323
|
mockRegistryResponse([
|
|
311
324
|
{ id: "claude-sonnet-4.6", apiModelId: "claude-sonnet-4-6", provider: "anthropic" },
|
|
@@ -359,4 +372,90 @@ describe("buildChatModel", () => {
|
|
|
359
372
|
await expect(buildChatModel({ modelName: "claude-sonnet-4.6" }))
|
|
360
373
|
.rejects.toThrow(/STIGMER_ANTHROPIC_BACKEND="verteks" is not a supported backend/);
|
|
361
374
|
});
|
|
375
|
+
|
|
376
|
+
describe("service tier (stigmer/stigmer#361)", () => {
|
|
377
|
+
// The #357 contract on the native harness: when the caller passes the
|
|
378
|
+
// execution's effective tier, every provider request pins it
|
|
379
|
+
// explicitly, so the provider ACCOUNT's default can never pick the
|
|
380
|
+
// price. Each provider has its own spelling; both are pinned here so
|
|
381
|
+
// a wrapper upgrade that moves the slot fails loudly.
|
|
382
|
+
|
|
383
|
+
it("OpenAI: STANDARD pins service_tier 'default' — never 'auto', which would re-open the account-default hole", async () => {
|
|
384
|
+
mockRegistryResponse([
|
|
385
|
+
{ id: "gpt-4.1", apiModelId: "gpt-4.1", provider: "openai" },
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
await buildChatModel({ modelName: "gpt-4.1", serviceTier: ServiceTier.STANDARD });
|
|
389
|
+
|
|
390
|
+
const args = mockOpenAICtor.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
|
391
|
+
expect(args).toMatchObject({ service_tier: "default" });
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it("OpenAI: FAST pins service_tier 'priority'", async () => {
|
|
395
|
+
mockRegistryResponse([
|
|
396
|
+
{ id: "gpt-4.1", apiModelId: "gpt-4.1", provider: "openai" },
|
|
397
|
+
]);
|
|
398
|
+
|
|
399
|
+
await buildChatModel({ modelName: "gpt-4.1", serviceTier: ServiceTier.FAST });
|
|
400
|
+
|
|
401
|
+
const args = mockOpenAICtor.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
|
402
|
+
expect(args).toMatchObject({ service_tier: "priority" });
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it("Anthropic: STANDARD rides invocationKwargs as 'standard_only' — priority capacity is never consumed implicitly", async () => {
|
|
406
|
+
mockRegistryResponse([
|
|
407
|
+
{ id: "claude-haiku-4.5", apiModelId: "claude-haiku-4-5-20251001", provider: "anthropic" },
|
|
408
|
+
]);
|
|
409
|
+
|
|
410
|
+
await buildChatModel({ modelName: "claude-haiku-4.5", serviceTier: ServiceTier.STANDARD });
|
|
411
|
+
|
|
412
|
+
expect(lastAnthropicArgs()).toMatchObject({
|
|
413
|
+
invocationKwargs: { service_tier: "standard_only" },
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("Anthropic: FAST rides invocationKwargs as 'auto' — use purchased priority capacity when available", async () => {
|
|
418
|
+
mockRegistryResponse([
|
|
419
|
+
{ id: "claude-haiku-4.5", apiModelId: "claude-haiku-4-5-20251001", provider: "anthropic" },
|
|
420
|
+
]);
|
|
421
|
+
|
|
422
|
+
await buildChatModel({ modelName: "claude-haiku-4.5", serviceTier: ServiceTier.FAST });
|
|
423
|
+
|
|
424
|
+
expect(lastAnthropicArgs()).toMatchObject({
|
|
425
|
+
invocationKwargs: { service_tier: "auto" },
|
|
426
|
+
});
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("an omitted tier sends NO tier parameter on either provider — platform-internal utility calls stay untiered", async () => {
|
|
430
|
+
mockRegistryResponse([
|
|
431
|
+
{ id: "gpt-4.1", apiModelId: "gpt-4.1", provider: "openai" },
|
|
432
|
+
]);
|
|
433
|
+
await buildChatModel({ modelName: "gpt-4.1" });
|
|
434
|
+
const openAiArgs = mockOpenAICtor.mock.calls.at(-1)?.[0] as Record<string, unknown>;
|
|
435
|
+
expect(openAiArgs).not.toHaveProperty("service_tier");
|
|
436
|
+
|
|
437
|
+
mockRegistryResponse([
|
|
438
|
+
{ id: "claude-haiku-4.5", apiModelId: "claude-haiku-4-5-20251001", provider: "anthropic" },
|
|
439
|
+
]);
|
|
440
|
+
await buildChatModel({ modelName: "claude-haiku-4.5" });
|
|
441
|
+
expect(lastAnthropicArgs()).not.toHaveProperty("invocationKwargs");
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it("the tier rides the proxy path too — the request body parameter is what the proxy meters", async () => {
|
|
445
|
+
mockRegistryResponse([
|
|
446
|
+
{ id: "claude-haiku-4.5", apiModelId: "claude-haiku-4-5-20251001", provider: "anthropic" },
|
|
447
|
+
]);
|
|
448
|
+
|
|
449
|
+
await buildChatModel({
|
|
450
|
+
modelName: "claude-haiku-4.5",
|
|
451
|
+
proxyEndpoint: "https://api.stigmer.ai",
|
|
452
|
+
stigmerToken: "token",
|
|
453
|
+
serviceTier: ServiceTier.STANDARD,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
expect(lastAnthropicArgs()).toMatchObject({
|
|
457
|
+
invocationKwargs: { service_tier: "standard_only" },
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
});
|
|
362
461
|
});
|
|
@@ -1,58 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Unit pins for the plan-mode rule builder
|
|
2
|
+
* Unit pins for the plan-mode rule builder.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Since issue #754 the rules carry ONLY plan mode's write-deny: the read
|
|
5
|
+
* boundary is structural (virtual-rooted backends confine every path to the
|
|
6
|
+
* workspace), so there is no workspace-root glob, no escaping, and no rule
|
|
7
|
+
* ordering left to protect. The single rule's SHAPE is still the security
|
|
8
|
+
* boundary for writes — "/**" must stay the match-all virtual pattern and
|
|
9
|
+
* the operation class must stay `write` (deepagents evaluates
|
|
10
|
+
* first-match-wins with a permissive default, so reads flow by omission).
|
|
11
|
+
* End-to-end behavior is pinned against the real deepagents runtime in
|
|
10
12
|
* execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import { describe, it, expect } from "vitest";
|
|
14
|
-
import {
|
|
15
|
-
buildPlanModePermissions,
|
|
16
|
-
escapeGlobLiteral,
|
|
17
|
-
} from "../plan-mode-permissions.js";
|
|
16
|
+
import { buildPlanModePermissions } from "../plan-mode-permissions.js";
|
|
18
17
|
|
|
19
18
|
describe("buildPlanModePermissions", () => {
|
|
20
|
-
it("builds the policy in
|
|
21
|
-
expect(buildPlanModePermissions(
|
|
22
|
-
{ operations: ["read"], paths: ["/ws/session-1/repo/**"] },
|
|
23
|
-
{ operations: ["read"], paths: ["/**"], mode: "deny" },
|
|
19
|
+
it("builds exactly the write-deny policy in the virtual dialect", () => {
|
|
20
|
+
expect(buildPlanModePermissions()).toEqual([
|
|
24
21
|
{ operations: ["write"], paths: ["/**"], mode: "deny" },
|
|
25
22
|
]);
|
|
26
23
|
});
|
|
27
24
|
|
|
28
|
-
it("
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// trailing-slash root would silently match nothing.
|
|
32
|
-
const [allow] = buildPlanModePermissions("/ws//session-1/repo/");
|
|
33
|
-
expect(allow.paths).toEqual(["/ws/session-1/repo/**"]);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("glob-escapes the root so special characters match literally", () => {
|
|
37
|
-
const [allow] = buildPlanModePermissions("/Users/x/My (work) [v2]");
|
|
38
|
-
expect(allow.paths).toEqual(["/Users/x/My \\(work\\) \\[v2\\]/**"]);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe("escapeGlobLiteral", () => {
|
|
43
|
-
it("escapes every micromatch metacharacter", () => {
|
|
44
|
-
expect(escapeGlobLiteral("a*b?c(d)e[f]g{h}i!j+k@l")).toBe(
|
|
45
|
-
"a\\*b\\?c\\(d\\)e\\[f\\]g\\{h\\}i\\!j\\+k\\@l",
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("escapes backslashes themselves", () => {
|
|
50
|
-
expect(escapeGlobLiteral("a\\b")).toBe("a\\\\b");
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("leaves plain path characters alone", () => {
|
|
54
|
-
expect(escapeGlobLiteral("/ws/session-1/repo.dir")).toBe(
|
|
55
|
-
"/ws/session-1/repo.dir",
|
|
56
|
-
);
|
|
25
|
+
it("carries no read rules — the read boundary is structural, not policy", () => {
|
|
26
|
+
const operations = buildPlanModePermissions().flatMap((rule) => rule.operations);
|
|
27
|
+
expect(operations).not.toContain("read");
|
|
57
28
|
});
|
|
58
29
|
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
captureRunnerSecrets,
|
|
4
|
+
getRunnerSecret,
|
|
5
|
+
setRunnerSecret,
|
|
6
|
+
runnerSecretsEnvView,
|
|
7
|
+
resetRunnerSecretsForTests,
|
|
8
|
+
} from "../runner-credential-store.js";
|
|
9
|
+
import {
|
|
10
|
+
RUNNER_CREDENTIAL_ENV_KEYS,
|
|
11
|
+
RUNNER_ENCRYPTION_ENV_KEYS,
|
|
12
|
+
RUNNER_SECRET_ENV_KEYS,
|
|
13
|
+
} from "../runner-credential-keys.js";
|
|
14
|
+
|
|
15
|
+
/** Snapshot/restore of every env slot these tests touch. */
|
|
16
|
+
const savedEnv = new Map<string, string | undefined>();
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
resetRunnerSecretsForTests();
|
|
20
|
+
for (const key of RUNNER_SECRET_ENV_KEYS) {
|
|
21
|
+
savedEnv.set(key, process.env[key]);
|
|
22
|
+
delete process.env[key];
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
resetRunnerSecretsForTests();
|
|
28
|
+
for (const [key, value] of savedEnv) {
|
|
29
|
+
if (value === undefined) {
|
|
30
|
+
delete process.env[key];
|
|
31
|
+
} else {
|
|
32
|
+
process.env[key] = value;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
savedEnv.clear();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("captureRunnerSecrets", () => {
|
|
39
|
+
it("moves every listed secret out of process.env and into the store", () => {
|
|
40
|
+
// Plant a value for every name in the source-of-truth list, so a key
|
|
41
|
+
// added to runner-credential-keys.ts is covered here automatically —
|
|
42
|
+
// the #385 test idiom, applied to custody.
|
|
43
|
+
for (const key of RUNNER_SECRET_ENV_KEYS) {
|
|
44
|
+
process.env[key] = `boot-${key}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
captureRunnerSecrets();
|
|
48
|
+
|
|
49
|
+
for (const key of RUNNER_SECRET_ENV_KEYS) {
|
|
50
|
+
expect(
|
|
51
|
+
process.env[key],
|
|
52
|
+
`'${key}' must not remain in process.env after capture — anything ` +
|
|
53
|
+
`left there is readable by every agent shell command (issue #508)`,
|
|
54
|
+
).toBeUndefined();
|
|
55
|
+
expect(getRunnerSecret(key)).toBe(`boot-${key}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("is idempotent — a second capture cannot re-freeze rotated values", () => {
|
|
60
|
+
process.env.STIGMER_TOKEN = "boot-token";
|
|
61
|
+
captureRunnerSecrets();
|
|
62
|
+
setRunnerSecret("STIGMER_TOKEN", "rotated-token");
|
|
63
|
+
|
|
64
|
+
// A late (buggy or racing) boot-door call must be a no-op.
|
|
65
|
+
process.env.STIGMER_TOKEN = "stale-replant";
|
|
66
|
+
captureRunnerSecrets();
|
|
67
|
+
|
|
68
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBe("rotated-token");
|
|
69
|
+
// The replant stays in env (capture did not consume it) — the live-env
|
|
70
|
+
// fallback below deliberately does NOT apply when the store holds a
|
|
71
|
+
// value, so rotation always wins.
|
|
72
|
+
expect(process.env.STIGMER_TOKEN).toBe("stale-replant");
|
|
73
|
+
delete process.env.STIGMER_TOKEN;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("scrubs the encryption keys, not only the #385 credentials (issue #508)", () => {
|
|
77
|
+
// Pins the #508 scope widening by name. The parameterized test above
|
|
78
|
+
// would pass even if the combined list regressed to the 8 credentials;
|
|
79
|
+
// this one cannot.
|
|
80
|
+
for (const key of [
|
|
81
|
+
"STIGMER_PAYLOAD_ENCRYPTION_KEY",
|
|
82
|
+
"STIGMER_PAYLOAD_ENCRYPTION_SECONDARY_KEY",
|
|
83
|
+
]) {
|
|
84
|
+
expect(RUNNER_ENCRYPTION_ENV_KEYS, `'${key}' must be a captured secret`).toContain(key);
|
|
85
|
+
}
|
|
86
|
+
for (const key of [...RUNNER_CREDENTIAL_ENV_KEYS, ...RUNNER_ENCRYPTION_ENV_KEYS]) {
|
|
87
|
+
expect(RUNNER_SECRET_ENV_KEYS, `'${key}' must be in the scrub list`).toContain(key);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("getRunnerSecret", () => {
|
|
93
|
+
it("falls back to a live process.env value the capture never saw", () => {
|
|
94
|
+
captureRunnerSecrets();
|
|
95
|
+
// Custody rule 2: a value planted after boot behaves like the env read
|
|
96
|
+
// the store replaced (test/embedder compatibility) — production only
|
|
97
|
+
// sets these at process start, so this path is dead there.
|
|
98
|
+
process.env.STIGMER_TOKEN = "late-planted";
|
|
99
|
+
|
|
100
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBe("late-planted");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("returns undefined for an absent secret", () => {
|
|
104
|
+
captureRunnerSecrets();
|
|
105
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBeUndefined();
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("setRunnerSecret", () => {
|
|
110
|
+
it("carries the rotation channel: set updates, null clears", () => {
|
|
111
|
+
captureRunnerSecrets();
|
|
112
|
+
|
|
113
|
+
setRunnerSecret("STIGMER_TOKEN", "minted-1");
|
|
114
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBe("minted-1");
|
|
115
|
+
|
|
116
|
+
setRunnerSecret("STIGMER_TOKEN", "minted-2");
|
|
117
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBe("minted-2");
|
|
118
|
+
|
|
119
|
+
setRunnerSecret("STIGMER_TOKEN", null);
|
|
120
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("never lets a rotated value sit in process.env, even if a writer beats the boot capture", () => {
|
|
124
|
+
process.env.STIGMER_TOKEN = "boot-token";
|
|
125
|
+
|
|
126
|
+
// No captureRunnerSecrets() call yet — the writer forces it.
|
|
127
|
+
setRunnerSecret("STIGMER_TOKEN", "rotated-early");
|
|
128
|
+
|
|
129
|
+
expect(process.env.STIGMER_TOKEN).toBeUndefined();
|
|
130
|
+
expect(getRunnerSecret("STIGMER_TOKEN")).toBe("rotated-early");
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("runnerSecretsEnvView", () => {
|
|
135
|
+
it("merges captured secrets over process.env without writing them back", () => {
|
|
136
|
+
process.env.STIGMER_TOKEN = "boot-token";
|
|
137
|
+
captureRunnerSecrets();
|
|
138
|
+
|
|
139
|
+
const view = runnerSecretsEnvView();
|
|
140
|
+
|
|
141
|
+
expect(view.STIGMER_TOKEN).toBe("boot-token");
|
|
142
|
+
expect(view.PATH).toBe(process.env.PATH);
|
|
143
|
+
expect(
|
|
144
|
+
process.env.STIGMER_TOKEN,
|
|
145
|
+
"building the view must not re-plant secrets into process.env",
|
|
146
|
+
).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("reflects rotation", () => {
|
|
150
|
+
captureRunnerSecrets();
|
|
151
|
+
setRunnerSecret("STIGMER_TOKEN", "rotated");
|
|
152
|
+
|
|
153
|
+
expect(runnerSecretsEnvView().STIGMER_TOKEN).toBe("rotated");
|
|
154
|
+
});
|
|
155
|
+
});
|