stagent 0.1.11 → 0.1.13

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.
Files changed (145) hide show
  1. package/README.md +74 -49
  2. package/package.json +3 -2
  3. package/public/readme/cost-usage-list.png +0 -0
  4. package/public/readme/dashboard-bulk-select.png +0 -0
  5. package/public/readme/dashboard-card-edit.png +0 -0
  6. package/public/readme/dashboard-create-form-ai-applied.png +0 -0
  7. package/public/readme/dashboard-create-form-ai-assist.png +0 -0
  8. package/public/readme/dashboard-create-form-empty.png +0 -0
  9. package/public/readme/dashboard-create-form-filled.png +0 -0
  10. package/public/readme/dashboard-filtered.png +0 -0
  11. package/public/readme/dashboard-list.png +0 -0
  12. package/public/readme/dashboard-workflow-confirm.png +0 -0
  13. package/public/readme/home-below-fold.png +0 -0
  14. package/public/readme/home-list.png +0 -0
  15. package/public/readme/inbox-list.png +0 -0
  16. package/public/readme/playbook-list.png +0 -0
  17. package/public/readme/profiles-list.png +0 -0
  18. package/public/readme/settings-list.png +0 -0
  19. package/public/readme/workflows-list.png +0 -0
  20. package/src/__tests__/e2e/blueprint.test.ts +63 -0
  21. package/src/__tests__/e2e/cross-runtime.test.ts +77 -0
  22. package/src/__tests__/e2e/helpers.ts +286 -0
  23. package/src/__tests__/e2e/parallel-workflow.test.ts +120 -0
  24. package/src/__tests__/e2e/sequence-workflow.test.ts +109 -0
  25. package/src/__tests__/e2e/setup.ts +156 -0
  26. package/src/__tests__/e2e/single-task.test.ts +170 -0
  27. package/src/app/api/command-palette/recent/route.ts +41 -18
  28. package/src/app/api/context/batch/route.ts +44 -0
  29. package/src/app/api/permissions/presets/route.ts +80 -0
  30. package/src/app/api/playbook/status/route.ts +15 -0
  31. package/src/app/api/profiles/route.ts +23 -20
  32. package/src/app/api/settings/pricing/route.ts +15 -0
  33. package/src/app/api/tasks/[id]/route.ts +54 -3
  34. package/src/app/api/workflows/[id]/route.ts +43 -4
  35. package/src/app/api/workflows/[id]/status/route.ts +70 -2
  36. package/src/app/api/workflows/from-assist/route.ts +6 -32
  37. package/src/app/costs/page.tsx +53 -43
  38. package/src/app/dashboard/page.tsx +59 -21
  39. package/src/app/documents/[id]/page.tsx +10 -8
  40. package/src/app/globals.css +11 -0
  41. package/src/app/page.tsx +60 -3
  42. package/src/app/playbook/[slug]/page.tsx +76 -0
  43. package/src/app/playbook/page.tsx +54 -0
  44. package/src/app/profiles/page.tsx +7 -4
  45. package/src/app/settings/page.tsx +2 -2
  46. package/src/app/tasks/[id]/page.tsx +22 -2
  47. package/src/components/costs/cost-dashboard.tsx +226 -320
  48. package/src/components/dashboard/activity-feed.tsx +6 -2
  49. package/src/components/dashboard/greeting.tsx +3 -1
  50. package/src/components/dashboard/priority-queue.tsx +58 -9
  51. package/src/components/dashboard/stats-cards.tsx +16 -2
  52. package/src/components/documents/document-chip-bar.tsx +183 -0
  53. package/src/components/documents/document-content-renderer.tsx +146 -0
  54. package/src/components/documents/document-detail-view.tsx +16 -239
  55. package/src/components/documents/image-zoom-view.tsx +60 -0
  56. package/src/components/documents/smart-extracted-text.tsx +47 -0
  57. package/src/components/documents/utils.ts +70 -0
  58. package/src/components/notifications/batch-proposal-review.tsx +150 -0
  59. package/src/components/notifications/inbox-list.tsx +4 -5
  60. package/src/components/notifications/notification-item.tsx +73 -6
  61. package/src/components/notifications/pending-approval-host.tsx +63 -14
  62. package/src/components/playbook/adoption-heatmap.tsx +69 -0
  63. package/src/components/playbook/journey-card.tsx +110 -0
  64. package/src/components/playbook/playbook-action-button.tsx +22 -0
  65. package/src/components/playbook/playbook-browser.tsx +143 -0
  66. package/src/components/playbook/playbook-card.tsx +102 -0
  67. package/src/components/playbook/playbook-detail-view.tsx +225 -0
  68. package/src/components/playbook/playbook-homepage.tsx +142 -0
  69. package/src/components/playbook/playbook-toc.tsx +90 -0
  70. package/src/components/playbook/playbook-updated-badge.tsx +23 -0
  71. package/src/components/playbook/related-docs.tsx +30 -0
  72. package/src/components/profiles/__tests__/learned-context-panel.test.tsx +175 -0
  73. package/src/components/profiles/context-proposal-review.tsx +7 -3
  74. package/src/components/profiles/learned-context-panel.tsx +116 -8
  75. package/src/components/profiles/profile-browser.tsx +1 -0
  76. package/src/components/profiles/profile-card.tsx +16 -8
  77. package/src/components/profiles/profile-detail-view.tsx +12 -4
  78. package/src/components/settings/__tests__/auth-config-section.test.tsx +147 -0
  79. package/src/components/settings/api-key-form.tsx +5 -43
  80. package/src/components/settings/auth-config-section.tsx +10 -6
  81. package/src/components/settings/auth-status-badge.tsx +8 -0
  82. package/src/components/settings/budget-guardrails-section.tsx +403 -620
  83. package/src/components/settings/connection-test-control.tsx +63 -0
  84. package/src/components/settings/permissions-section.tsx +85 -75
  85. package/src/components/settings/permissions-sections.tsx +24 -0
  86. package/src/components/settings/presets-section.tsx +159 -0
  87. package/src/components/settings/pricing-registry-panel.tsx +164 -0
  88. package/src/components/shared/app-sidebar.tsx +4 -2
  89. package/src/components/shared/command-palette.tsx +30 -0
  90. package/src/components/shared/light-markdown.tsx +134 -0
  91. package/src/components/tasks/__tests__/kanban-board-accessibility.test.tsx +1 -1
  92. package/src/components/tasks/ai-assist-panel.tsx +108 -78
  93. package/src/components/tasks/content-preview.tsx +2 -1
  94. package/src/components/tasks/kanban-board.tsx +57 -5
  95. package/src/components/tasks/kanban-column.tsx +34 -23
  96. package/src/components/tasks/task-bento-cell.tsx +50 -0
  97. package/src/components/tasks/task-bento-grid.tsx +155 -0
  98. package/src/components/tasks/task-card.tsx +14 -16
  99. package/src/components/tasks/task-chip-bar.tsx +207 -0
  100. package/src/components/tasks/task-detail-view.tsx +42 -190
  101. package/src/components/tasks/task-result-renderer.tsx +33 -0
  102. package/src/components/workflows/blueprint-gallery.tsx +19 -12
  103. package/src/components/workflows/blueprint-preview.tsx +8 -1
  104. package/src/components/workflows/loop-status-view.tsx +2 -4
  105. package/src/components/workflows/swarm-dashboard.tsx +2 -3
  106. package/src/components/workflows/workflow-confirmation-view.tsx +2 -7
  107. package/src/components/workflows/workflow-full-output.tsx +80 -0
  108. package/src/components/workflows/workflow-kanban-card.tsx +121 -0
  109. package/src/components/workflows/workflow-list.tsx +47 -42
  110. package/src/components/workflows/workflow-status-view.tsx +163 -16
  111. package/src/lib/agents/learned-context.ts +27 -15
  112. package/src/lib/agents/learning-session.ts +354 -0
  113. package/src/lib/agents/pattern-extractor.ts +19 -0
  114. package/src/lib/agents/profiles/__tests__/sort.test.ts +42 -0
  115. package/src/lib/agents/profiles/sort.ts +7 -0
  116. package/src/lib/constants/card-icons.tsx +202 -0
  117. package/src/lib/constants/prose-styles.ts +7 -0
  118. package/src/lib/constants/settings.ts +1 -0
  119. package/src/lib/constants/task-status.ts +3 -0
  120. package/src/lib/db/schema.ts +3 -0
  121. package/src/lib/docs/adoption.ts +105 -0
  122. package/src/lib/docs/journey-tracker.ts +21 -0
  123. package/src/lib/docs/reader.ts +107 -0
  124. package/src/lib/docs/types.ts +54 -0
  125. package/src/lib/docs/usage-stage.ts +60 -0
  126. package/src/lib/documents/context-builder.ts +41 -0
  127. package/src/lib/notifications/actionable.ts +18 -10
  128. package/src/lib/queries/chart-data.ts +20 -1
  129. package/src/lib/settings/__tests__/budget-guardrails.test.ts +86 -24
  130. package/src/lib/settings/budget-guardrails.ts +213 -85
  131. package/src/lib/settings/permission-presets.ts +150 -0
  132. package/src/lib/settings/runtime-setup.ts +71 -0
  133. package/src/lib/usage/__tests__/ledger.test.ts +2 -2
  134. package/src/lib/usage/__tests__/pricing-registry.test.ts +78 -0
  135. package/src/lib/usage/ledger.ts +1 -1
  136. package/src/lib/usage/pricing-registry.ts +570 -0
  137. package/src/lib/usage/pricing.ts +15 -95
  138. package/src/lib/utils/__tests__/learned-context-history.test.ts +171 -0
  139. package/src/lib/utils/learned-context-history.ts +150 -0
  140. package/src/lib/validators/__tests__/settings.test.ts +23 -16
  141. package/src/lib/validators/settings.ts +3 -9
  142. package/src/lib/workflows/engine.ts +75 -61
  143. package/src/lib/workflows/types.ts +2 -0
  144. package/tsconfig.json +2 -1
  145. package/src/components/documents/document-preview.tsx +0 -68
@@ -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("registry-2026-03-15");
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("registry-2026-03-15-fallback");
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
+ });
@@ -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,