stagent 0.1.11 → 0.1.12
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 +35 -4
- package/package.json +3 -2
- package/src/__tests__/e2e/blueprint.test.ts +63 -0
- package/src/__tests__/e2e/cross-runtime.test.ts +77 -0
- package/src/__tests__/e2e/helpers.ts +286 -0
- package/src/__tests__/e2e/parallel-workflow.test.ts +120 -0
- package/src/__tests__/e2e/sequence-workflow.test.ts +109 -0
- package/src/__tests__/e2e/setup.ts +156 -0
- package/src/__tests__/e2e/single-task.test.ts +170 -0
- package/src/app/api/command-palette/recent/route.ts +41 -18
- package/src/app/api/context/batch/route.ts +44 -0
- package/src/app/api/permissions/presets/route.ts +80 -0
- package/src/app/api/playbook/status/route.ts +15 -0
- package/src/app/api/profiles/route.ts +23 -20
- package/src/app/api/settings/pricing/route.ts +15 -0
- package/src/app/costs/page.tsx +53 -43
- package/src/app/playbook/[slug]/page.tsx +76 -0
- package/src/app/playbook/page.tsx +54 -0
- package/src/app/profiles/page.tsx +7 -4
- package/src/app/settings/page.tsx +2 -2
- package/src/components/costs/cost-dashboard.tsx +226 -320
- package/src/components/dashboard/activity-feed.tsx +6 -2
- package/src/components/notifications/batch-proposal-review.tsx +150 -0
- package/src/components/notifications/notification-item.tsx +6 -3
- package/src/components/notifications/pending-approval-host.tsx +57 -11
- package/src/components/playbook/adoption-heatmap.tsx +69 -0
- package/src/components/playbook/journey-card.tsx +110 -0
- package/src/components/playbook/playbook-action-button.tsx +22 -0
- package/src/components/playbook/playbook-browser.tsx +143 -0
- package/src/components/playbook/playbook-card.tsx +102 -0
- package/src/components/playbook/playbook-detail-view.tsx +223 -0
- package/src/components/playbook/playbook-homepage.tsx +142 -0
- package/src/components/playbook/playbook-toc.tsx +90 -0
- package/src/components/playbook/playbook-updated-badge.tsx +23 -0
- package/src/components/playbook/related-docs.tsx +30 -0
- package/src/components/profiles/__tests__/learned-context-panel.test.tsx +175 -0
- package/src/components/profiles/context-proposal-review.tsx +7 -3
- package/src/components/profiles/learned-context-panel.tsx +116 -8
- package/src/components/profiles/profile-detail-view.tsx +6 -3
- package/src/components/settings/__tests__/auth-config-section.test.tsx +147 -0
- package/src/components/settings/api-key-form.tsx +5 -43
- package/src/components/settings/auth-config-section.tsx +10 -6
- package/src/components/settings/auth-status-badge.tsx +8 -0
- package/src/components/settings/budget-guardrails-section.tsx +403 -620
- package/src/components/settings/connection-test-control.tsx +63 -0
- package/src/components/settings/permissions-section.tsx +85 -75
- package/src/components/settings/permissions-sections.tsx +24 -0
- package/src/components/settings/presets-section.tsx +159 -0
- package/src/components/settings/pricing-registry-panel.tsx +164 -0
- package/src/components/shared/app-sidebar.tsx +2 -0
- package/src/components/shared/command-palette.tsx +30 -0
- package/src/components/shared/light-markdown.tsx +134 -0
- package/src/components/workflows/loop-status-view.tsx +8 -4
- package/src/components/workflows/workflow-status-view.tsx +16 -9
- package/src/lib/agents/learned-context.ts +27 -15
- package/src/lib/agents/learning-session.ts +234 -0
- package/src/lib/agents/pattern-extractor.ts +19 -0
- package/src/lib/agents/profiles/__tests__/sort.test.ts +42 -0
- package/src/lib/agents/profiles/sort.ts +7 -0
- package/src/lib/constants/settings.ts +1 -0
- package/src/lib/db/schema.ts +3 -0
- package/src/lib/docs/adoption.ts +105 -0
- package/src/lib/docs/journey-tracker.ts +21 -0
- package/src/lib/docs/reader.ts +102 -0
- package/src/lib/docs/types.ts +54 -0
- package/src/lib/docs/usage-stage.ts +60 -0
- package/src/lib/notifications/actionable.ts +18 -10
- package/src/lib/settings/__tests__/budget-guardrails.test.ts +86 -24
- package/src/lib/settings/budget-guardrails.ts +213 -85
- package/src/lib/settings/permission-presets.ts +150 -0
- package/src/lib/settings/runtime-setup.ts +71 -0
- package/src/lib/usage/__tests__/ledger.test.ts +2 -2
- package/src/lib/usage/__tests__/pricing-registry.test.ts +78 -0
- package/src/lib/usage/ledger.ts +1 -1
- package/src/lib/usage/pricing-registry.ts +570 -0
- package/src/lib/usage/pricing.ts +15 -95
- package/src/lib/utils/__tests__/learned-context-history.test.ts +171 -0
- package/src/lib/utils/learned-context-history.ts +150 -0
- package/src/lib/validators/__tests__/settings.test.ts +23 -16
- package/src/lib/validators/settings.ts +3 -9
- package/src/lib/workflows/engine.ts +18 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRuntimeCatalogEntry,
|
|
3
|
+
SUPPORTED_AGENT_RUNTIMES,
|
|
4
|
+
type AgentRuntimeId,
|
|
5
|
+
} from "@/lib/agents/runtime/catalog";
|
|
6
|
+
import { getAuthSettings } from "./auth";
|
|
7
|
+
import { getOpenAIAuthSettings } from "./openai-auth";
|
|
8
|
+
import type { ApiKeySource, AuthMethod } from "@/lib/constants/settings";
|
|
9
|
+
|
|
10
|
+
export type RuntimeBillingMode = "usage" | "subscription";
|
|
11
|
+
export type RuntimeSetupMethod = AuthMethod | "none";
|
|
12
|
+
|
|
13
|
+
export interface RuntimeSetupState {
|
|
14
|
+
runtimeId: AgentRuntimeId;
|
|
15
|
+
label: string;
|
|
16
|
+
providerId: "anthropic" | "openai";
|
|
17
|
+
configured: boolean;
|
|
18
|
+
authMethod: RuntimeSetupMethod;
|
|
19
|
+
apiKeySource: ApiKeySource;
|
|
20
|
+
billingMode: RuntimeBillingMode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getRuntimeSetupStates(): Promise<
|
|
24
|
+
Record<AgentRuntimeId, RuntimeSetupState>
|
|
25
|
+
> {
|
|
26
|
+
const [claudeAuth, openAIAuth] = await Promise.all([
|
|
27
|
+
getAuthSettings(),
|
|
28
|
+
getOpenAIAuthSettings(),
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const claudeRuntime = getRuntimeCatalogEntry("claude-code");
|
|
32
|
+
const openAIRuntime = getRuntimeCatalogEntry("openai-codex-app-server");
|
|
33
|
+
|
|
34
|
+
const claudeAuthMethod: RuntimeSetupMethod =
|
|
35
|
+
claudeAuth.method === "oauth" || claudeAuth.apiKeySource === "oauth"
|
|
36
|
+
? "oauth"
|
|
37
|
+
: claudeAuth.hasKey
|
|
38
|
+
? "api_key"
|
|
39
|
+
: "none";
|
|
40
|
+
const claudeConfigured =
|
|
41
|
+
claudeAuth.hasKey || claudeAuth.apiKeySource === "oauth";
|
|
42
|
+
|
|
43
|
+
const states = {
|
|
44
|
+
"claude-code": {
|
|
45
|
+
runtimeId: "claude-code",
|
|
46
|
+
label: claudeRuntime.label,
|
|
47
|
+
providerId: claudeRuntime.providerId,
|
|
48
|
+
configured: claudeConfigured,
|
|
49
|
+
authMethod: claudeAuthMethod,
|
|
50
|
+
apiKeySource: claudeAuth.apiKeySource,
|
|
51
|
+
billingMode: claudeAuthMethod === "oauth" ? "subscription" : "usage",
|
|
52
|
+
},
|
|
53
|
+
"openai-codex-app-server": {
|
|
54
|
+
runtimeId: "openai-codex-app-server",
|
|
55
|
+
label: openAIRuntime.label,
|
|
56
|
+
providerId: openAIRuntime.providerId,
|
|
57
|
+
configured: openAIAuth.hasKey,
|
|
58
|
+
authMethod: openAIAuth.hasKey ? "api_key" : "none",
|
|
59
|
+
apiKeySource: openAIAuth.apiKeySource,
|
|
60
|
+
billingMode: "usage",
|
|
61
|
+
},
|
|
62
|
+
} satisfies Record<AgentRuntimeId, RuntimeSetupState>;
|
|
63
|
+
|
|
64
|
+
return states;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function listConfiguredRuntimeIds(
|
|
68
|
+
states: Record<AgentRuntimeId, RuntimeSetupState>
|
|
69
|
+
) {
|
|
70
|
+
return SUPPORTED_AGENT_RUNTIMES.filter((runtimeId) => states[runtimeId].configured);
|
|
71
|
+
}
|
|
@@ -85,13 +85,13 @@ describe("usage ledger", () => {
|
|
|
85
85
|
const priced = rows.find((row) => row.modelId === "claude-sonnet-4-20250514");
|
|
86
86
|
expect(priced?.costMicros).toBe(10_500);
|
|
87
87
|
expect(priced?.status).toBe("completed");
|
|
88
|
-
expect(priced?.pricingVersion).toBe("
|
|
88
|
+
expect(priced?.pricingVersion).toBe("anthropic-claude-sonnet");
|
|
89
89
|
|
|
90
90
|
// Unknown model: fallback pricing (conservative Opus-tier for OpenAI: $10/$30)
|
|
91
91
|
const fallback = rows.find((row) => row.modelId === "codex-unknown");
|
|
92
92
|
expect(fallback?.costMicros).toBeGreaterThan(0);
|
|
93
93
|
expect(fallback?.status).toBe("completed");
|
|
94
|
-
expect(fallback?.pricingVersion).toBe("
|
|
94
|
+
expect(fallback?.pricingVersion).toBe("openai-fallback");
|
|
95
95
|
|
|
96
96
|
// Null modelId: truly unknown
|
|
97
97
|
const unknown = rows.find((row) => row.modelId === null);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
|
|
6
|
+
let tempDir: string;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tempDir = mkdtempSync(join(tmpdir(), "stagent-pricing-registry-"));
|
|
10
|
+
vi.resetModules();
|
|
11
|
+
vi.stubEnv("STAGENT_DATA_DIR", tempDir);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.unstubAllEnvs();
|
|
16
|
+
vi.unstubAllGlobals();
|
|
17
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("pricing registry", () => {
|
|
21
|
+
it("refreshes pricing from official provider pages and updates visible rows", async () => {
|
|
22
|
+
const fetch = vi.fn(async (input: string) => {
|
|
23
|
+
if (input.includes("anthropic.com")) {
|
|
24
|
+
return new Response(`
|
|
25
|
+
<html>
|
|
26
|
+
<body>
|
|
27
|
+
Claude Sonnet 4 $3 / 1M input tokens $15 / 1M output tokens
|
|
28
|
+
Claude Opus 4 $15 / 1M input tokens $75 / 1M output tokens
|
|
29
|
+
Claude Haiku 3.5 $0.80 / 1M input tokens $4 / 1M output tokens
|
|
30
|
+
Claude Pro $20
|
|
31
|
+
Max 5x $100
|
|
32
|
+
Max 20x $200
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
35
|
+
`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return new Response(`
|
|
39
|
+
<html>
|
|
40
|
+
<body>
|
|
41
|
+
GPT-5 $10 / 1M input tokens $30 / 1M output tokens
|
|
42
|
+
GPT-4o $2.50 / 1M input tokens $10 / 1M output tokens
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
vi.stubGlobal("fetch", fetch);
|
|
48
|
+
|
|
49
|
+
const { refreshPricingRegistry } = await import("../pricing-registry");
|
|
50
|
+
const snapshot = await refreshPricingRegistry();
|
|
51
|
+
|
|
52
|
+
expect(snapshot.providers.anthropic.rows.find((row) => row.key === "anthropic-plan-pro")?.monthlyPriceUsd).toBe(20);
|
|
53
|
+
expect(snapshot.providers.anthropic.rows.find((row) => row.key === "anthropic-claude-sonnet")?.inputCostPerMillionMicros).toBe(3_000_000);
|
|
54
|
+
expect(snapshot.providers.openai.rows.find((row) => row.key === "openai-gpt-5")?.outputCostPerMillionMicros).toBe(30_000_000);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("keeps last-known-good pricing when a refresh fails", async () => {
|
|
58
|
+
const fetch = vi.fn(async (input: string) => {
|
|
59
|
+
if (input.includes("anthropic.com")) {
|
|
60
|
+
return new Response(`<html><body>Claude Pro $20</body></html>`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
throw new Error("network down");
|
|
64
|
+
});
|
|
65
|
+
vi.stubGlobal("fetch", fetch);
|
|
66
|
+
|
|
67
|
+
const { refreshPricingRegistry, getPricingRegistrySnapshot } = await import(
|
|
68
|
+
"../pricing-registry"
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
await refreshPricingRegistry();
|
|
72
|
+
const snapshot = await getPricingRegistrySnapshot();
|
|
73
|
+
|
|
74
|
+
expect(snapshot.providers.anthropic.refreshError).toBeNull();
|
|
75
|
+
expect(snapshot.providers.openai.refreshError).toContain("network down");
|
|
76
|
+
expect(snapshot.providers.openai.rows.find((row) => row.key === "openai-gpt-5")?.inputCostPerMillionMicros).toBe(10_000_000);
|
|
77
|
+
});
|
|
78
|
+
});
|
package/src/lib/usage/ledger.ts
CHANGED
|
@@ -207,7 +207,7 @@ export async function recordUsageLedgerEntry(input: UsageLedgerWriteInput) {
|
|
|
207
207
|
(normalizedInputTokens != null && normalizedOutputTokens != null
|
|
208
208
|
? normalizedInputTokens + normalizedOutputTokens
|
|
209
209
|
: null);
|
|
210
|
-
const { costMicros, pricingVersion } = deriveUsageCostMicros({
|
|
210
|
+
const { costMicros, pricingVersion } = await deriveUsageCostMicros({
|
|
211
211
|
providerId: input.providerId,
|
|
212
212
|
modelId: input.modelId,
|
|
213
213
|
inputTokens: normalizedInputTokens,
|