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,171 @@
|
|
|
1
|
+
import type { LearnedContextRow } from "@/lib/db/schema";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
buildLearnedContextHistoryEntries,
|
|
5
|
+
buildUnifiedDiff,
|
|
6
|
+
hasMeaningfulDerivedDiff,
|
|
7
|
+
} from "../learned-context-history";
|
|
8
|
+
|
|
9
|
+
function makeRow(
|
|
10
|
+
overrides: Partial<LearnedContextRow> & {
|
|
11
|
+
id: string;
|
|
12
|
+
version: number;
|
|
13
|
+
changeType: LearnedContextRow["changeType"];
|
|
14
|
+
}
|
|
15
|
+
): LearnedContextRow {
|
|
16
|
+
return {
|
|
17
|
+
id: overrides.id,
|
|
18
|
+
profileId: overrides.profileId ?? "general",
|
|
19
|
+
version: overrides.version,
|
|
20
|
+
content: overrides.content ?? null,
|
|
21
|
+
diff: overrides.diff ?? null,
|
|
22
|
+
changeType: overrides.changeType,
|
|
23
|
+
sourceTaskId: overrides.sourceTaskId ?? null,
|
|
24
|
+
proposalNotificationId: overrides.proposalNotificationId ?? null,
|
|
25
|
+
proposedAdditions: overrides.proposedAdditions ?? null,
|
|
26
|
+
approvedBy: overrides.approvedBy ?? null,
|
|
27
|
+
createdAt: overrides.createdAt ?? new Date("2026-03-17T12:00:00.000Z"),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
describe("buildUnifiedDiff", () => {
|
|
32
|
+
it("treats the first version as all additions", () => {
|
|
33
|
+
expect(buildUnifiedDiff(null, "Alpha\nBeta")).toEqual([
|
|
34
|
+
{ kind: "added", value: "Alpha" },
|
|
35
|
+
{ kind: "added", value: "Beta" },
|
|
36
|
+
]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("builds a unified diff for appended content", () => {
|
|
40
|
+
expect(buildUnifiedDiff("Alpha", "Alpha\nBeta")).toEqual([
|
|
41
|
+
{ kind: "context", value: "Alpha" },
|
|
42
|
+
{ kind: "added", value: "Beta" },
|
|
43
|
+
]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe("buildLearnedContextHistoryEntries", () => {
|
|
48
|
+
it("derives rollback diffs from the previous snapshot version", () => {
|
|
49
|
+
const history = [
|
|
50
|
+
makeRow({
|
|
51
|
+
id: "v3",
|
|
52
|
+
version: 3,
|
|
53
|
+
changeType: "rollback",
|
|
54
|
+
content: "Alpha",
|
|
55
|
+
diff: "Rolled back to version 1",
|
|
56
|
+
}),
|
|
57
|
+
makeRow({
|
|
58
|
+
id: "v2",
|
|
59
|
+
version: 2,
|
|
60
|
+
changeType: "approved",
|
|
61
|
+
content: "Alpha\nBeta",
|
|
62
|
+
diff: "Beta",
|
|
63
|
+
}),
|
|
64
|
+
makeRow({
|
|
65
|
+
id: "v1",
|
|
66
|
+
version: 1,
|
|
67
|
+
changeType: "approved",
|
|
68
|
+
content: "Alpha",
|
|
69
|
+
diff: "Alpha",
|
|
70
|
+
}),
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const entries = buildLearnedContextHistoryEntries(history);
|
|
74
|
+
const rollbackEntry = entries[0];
|
|
75
|
+
|
|
76
|
+
expect(rollbackEntry.snapshotContent).toBe("Alpha");
|
|
77
|
+
expect(rollbackEntry.derivedDiff).toEqual({
|
|
78
|
+
previousVersion: 2,
|
|
79
|
+
lines: [
|
|
80
|
+
{ kind: "context", value: "Alpha" },
|
|
81
|
+
{ kind: "removed", value: "Beta" },
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("derives summarization diffs from the previous snapshot version", () => {
|
|
87
|
+
const history = [
|
|
88
|
+
makeRow({
|
|
89
|
+
id: "v3",
|
|
90
|
+
version: 3,
|
|
91
|
+
changeType: "summarization",
|
|
92
|
+
content: "Alpha\nGamma",
|
|
93
|
+
diff: "Summarized from 14 to 11 chars",
|
|
94
|
+
}),
|
|
95
|
+
makeRow({
|
|
96
|
+
id: "v2",
|
|
97
|
+
version: 2,
|
|
98
|
+
changeType: "rejected",
|
|
99
|
+
content: "Alpha\nBeta",
|
|
100
|
+
diff: "Rejected addition",
|
|
101
|
+
}),
|
|
102
|
+
makeRow({
|
|
103
|
+
id: "v1",
|
|
104
|
+
version: 1,
|
|
105
|
+
changeType: "approved",
|
|
106
|
+
content: "Alpha\nBeta",
|
|
107
|
+
diff: "Alpha\nBeta",
|
|
108
|
+
}),
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const entries = buildLearnedContextHistoryEntries(history);
|
|
112
|
+
const summarizationEntry = entries[0];
|
|
113
|
+
|
|
114
|
+
expect(summarizationEntry.derivedDiff).toEqual({
|
|
115
|
+
previousVersion: 1,
|
|
116
|
+
lines: [
|
|
117
|
+
{ kind: "context", value: "Alpha" },
|
|
118
|
+
{ kind: "removed", value: "Beta" },
|
|
119
|
+
{ kind: "added", value: "Gamma" },
|
|
120
|
+
],
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("does not create derived diffs for proposal-only rows", () => {
|
|
125
|
+
const history = [
|
|
126
|
+
makeRow({
|
|
127
|
+
id: "proposal",
|
|
128
|
+
version: 2,
|
|
129
|
+
changeType: "proposal",
|
|
130
|
+
diff: "Add retries",
|
|
131
|
+
}),
|
|
132
|
+
makeRow({
|
|
133
|
+
id: "approved",
|
|
134
|
+
version: 1,
|
|
135
|
+
changeType: "approved",
|
|
136
|
+
content: "Validate inputs",
|
|
137
|
+
diff: "Validate inputs",
|
|
138
|
+
}),
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const entries = buildLearnedContextHistoryEntries(history);
|
|
142
|
+
|
|
143
|
+
expect(entries[0].snapshotContent).toBeNull();
|
|
144
|
+
expect(entries[0].derivedDiff).toBeNull();
|
|
145
|
+
expect(entries[1].derivedDiff?.previousVersion).toBeNull();
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe("hasMeaningfulDerivedDiff", () => {
|
|
150
|
+
it("detects when a derived diff contains additions or removals", () => {
|
|
151
|
+
expect(
|
|
152
|
+
hasMeaningfulDerivedDiff({
|
|
153
|
+
previousVersion: 1,
|
|
154
|
+
lines: [
|
|
155
|
+
{ kind: "context", value: "Alpha" },
|
|
156
|
+
{ kind: "added", value: "Beta" },
|
|
157
|
+
],
|
|
158
|
+
})
|
|
159
|
+
).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("returns false for null or unchanged diffs", () => {
|
|
163
|
+
expect(hasMeaningfulDerivedDiff(null)).toBe(false);
|
|
164
|
+
expect(
|
|
165
|
+
hasMeaningfulDerivedDiff({
|
|
166
|
+
previousVersion: 1,
|
|
167
|
+
lines: [{ kind: "context", value: "Alpha" }],
|
|
168
|
+
})
|
|
169
|
+
).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { LearnedContextRow } from "@/lib/db/schema";
|
|
2
|
+
|
|
3
|
+
export type LearnedContextSnapshotType =
|
|
4
|
+
| "approved"
|
|
5
|
+
| "rollback"
|
|
6
|
+
| "summarization";
|
|
7
|
+
|
|
8
|
+
export type LearnedContextDiffKind = "context" | "added" | "removed";
|
|
9
|
+
|
|
10
|
+
export interface LearnedContextDiffLine {
|
|
11
|
+
kind: LearnedContextDiffKind;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LearnedContextDerivedDiff {
|
|
16
|
+
previousVersion: number | null;
|
|
17
|
+
lines: LearnedContextDiffLine[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface LearnedContextHistoryEntry {
|
|
21
|
+
row: LearnedContextRow;
|
|
22
|
+
snapshotContent: string | null;
|
|
23
|
+
derivedDiff: LearnedContextDerivedDiff | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SNAPSHOT_CHANGE_TYPES = new Set<LearnedContextSnapshotType>([
|
|
27
|
+
"approved",
|
|
28
|
+
"rollback",
|
|
29
|
+
"summarization",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
export function isSnapshotVersion(
|
|
33
|
+
row: LearnedContextRow
|
|
34
|
+
): row is LearnedContextRow & {
|
|
35
|
+
changeType: LearnedContextSnapshotType;
|
|
36
|
+
content: string;
|
|
37
|
+
} {
|
|
38
|
+
return (
|
|
39
|
+
SNAPSHOT_CHANGE_TYPES.has(row.changeType as LearnedContextSnapshotType) &&
|
|
40
|
+
typeof row.content === "string"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeLines(content: string | null): string[] {
|
|
45
|
+
if (!content) return [];
|
|
46
|
+
return content.replace(/\r\n/g, "\n").split("\n");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function buildLcsTable(previous: string[], next: string[]): number[][] {
|
|
50
|
+
const table = Array.from({ length: previous.length + 1 }, () =>
|
|
51
|
+
Array(next.length + 1).fill(0)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
for (let i = previous.length - 1; i >= 0; i -= 1) {
|
|
55
|
+
for (let j = next.length - 1; j >= 0; j -= 1) {
|
|
56
|
+
if (previous[i] === next[j]) {
|
|
57
|
+
table[i][j] = table[i + 1][j + 1] + 1;
|
|
58
|
+
} else {
|
|
59
|
+
table[i][j] = Math.max(table[i + 1][j], table[i][j + 1]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return table;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function buildUnifiedDiff(
|
|
68
|
+
previousContent: string | null,
|
|
69
|
+
nextContent: string
|
|
70
|
+
): LearnedContextDiffLine[] {
|
|
71
|
+
const previousLines = normalizeLines(previousContent);
|
|
72
|
+
const nextLines = normalizeLines(nextContent);
|
|
73
|
+
|
|
74
|
+
if (previousLines.length === 0) {
|
|
75
|
+
return nextLines.map((value) => ({ kind: "added", value }));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const table = buildLcsTable(previousLines, nextLines);
|
|
79
|
+
const lines: LearnedContextDiffLine[] = [];
|
|
80
|
+
|
|
81
|
+
let i = 0;
|
|
82
|
+
let j = 0;
|
|
83
|
+
|
|
84
|
+
while (i < previousLines.length && j < nextLines.length) {
|
|
85
|
+
if (previousLines[i] === nextLines[j]) {
|
|
86
|
+
lines.push({ kind: "context", value: previousLines[i] });
|
|
87
|
+
i += 1;
|
|
88
|
+
j += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (table[i + 1][j] >= table[i][j + 1]) {
|
|
93
|
+
lines.push({ kind: "removed", value: previousLines[i] });
|
|
94
|
+
i += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lines.push({ kind: "added", value: nextLines[j] });
|
|
99
|
+
j += 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
while (i < previousLines.length) {
|
|
103
|
+
lines.push({ kind: "removed", value: previousLines[i] });
|
|
104
|
+
i += 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
while (j < nextLines.length) {
|
|
108
|
+
lines.push({ kind: "added", value: nextLines[j] });
|
|
109
|
+
j += 1;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return lines;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function hasMeaningfulDerivedDiff(
|
|
116
|
+
diff: LearnedContextDerivedDiff | null
|
|
117
|
+
): boolean {
|
|
118
|
+
return Boolean(
|
|
119
|
+
diff?.lines.some((line) => line.kind === "added" || line.kind === "removed")
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function buildLearnedContextHistoryEntries(
|
|
124
|
+
history: LearnedContextRow[]
|
|
125
|
+
): LearnedContextHistoryEntry[] {
|
|
126
|
+
const derivedDiffById = new Map<string, LearnedContextDerivedDiff>();
|
|
127
|
+
let previousSnapshot: (LearnedContextRow & {
|
|
128
|
+
changeType: LearnedContextSnapshotType;
|
|
129
|
+
content: string;
|
|
130
|
+
}) | null = null;
|
|
131
|
+
|
|
132
|
+
const ascending = [...history].sort((a, b) => a.version - b.version);
|
|
133
|
+
|
|
134
|
+
for (const row of ascending) {
|
|
135
|
+
if (!isSnapshotVersion(row)) continue;
|
|
136
|
+
|
|
137
|
+
derivedDiffById.set(row.id, {
|
|
138
|
+
previousVersion: previousSnapshot?.version ?? null,
|
|
139
|
+
lines: buildUnifiedDiff(previousSnapshot?.content ?? null, row.content),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
previousSnapshot = row;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return history.map((row) => ({
|
|
146
|
+
row,
|
|
147
|
+
snapshotContent: isSnapshotVersion(row) ? row.content : null,
|
|
148
|
+
derivedDiff: derivedDiffById.get(row.id) ?? null,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
@@ -99,24 +99,18 @@ describe("updateOpenAISettingsSchema", () => {
|
|
|
99
99
|
});
|
|
100
100
|
|
|
101
101
|
describe("updateBudgetPolicySchema", () => {
|
|
102
|
-
it("accepts
|
|
102
|
+
it("accepts a monthly-only budget payload", () => {
|
|
103
103
|
const result = updateBudgetPolicySchema.safeParse({
|
|
104
104
|
overall: {
|
|
105
|
-
dailySpendCapUsd: null,
|
|
106
105
|
monthlySpendCapUsd: 50,
|
|
107
106
|
},
|
|
108
107
|
runtimes: {
|
|
109
108
|
"claude-code": {
|
|
110
|
-
dailySpendCapUsd: 10,
|
|
111
109
|
monthlySpendCapUsd: 100,
|
|
112
|
-
|
|
113
|
-
monthlyTokenCap: null,
|
|
110
|
+
claudeOAuthPlan: "max_5x",
|
|
114
111
|
},
|
|
115
112
|
"openai-codex-app-server": {
|
|
116
|
-
dailySpendCapUsd: null,
|
|
117
113
|
monthlySpendCapUsd: null,
|
|
118
|
-
dailyTokenCap: null,
|
|
119
|
-
monthlyTokenCap: 50000,
|
|
120
114
|
},
|
|
121
115
|
},
|
|
122
116
|
});
|
|
@@ -127,21 +121,34 @@ describe("updateBudgetPolicySchema", () => {
|
|
|
127
121
|
it("rejects zero and negative values", () => {
|
|
128
122
|
const result = updateBudgetPolicySchema.safeParse({
|
|
129
123
|
overall: {
|
|
130
|
-
dailySpendCapUsd: 0,
|
|
131
124
|
monthlySpendCapUsd: -1,
|
|
132
125
|
},
|
|
133
126
|
runtimes: {
|
|
134
127
|
"claude-code": {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
dailyTokenCap: 0,
|
|
138
|
-
monthlyTokenCap: null,
|
|
128
|
+
monthlySpendCapUsd: 0,
|
|
129
|
+
claudeOAuthPlan: "pro",
|
|
139
130
|
},
|
|
140
131
|
"openai-codex-app-server": {
|
|
141
|
-
dailySpendCapUsd: null,
|
|
142
132
|
monthlySpendCapUsd: null,
|
|
143
|
-
|
|
144
|
-
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(result.success).toBe(false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("rejects invalid Claude OAuth plans", () => {
|
|
141
|
+
const result = updateBudgetPolicySchema.safeParse({
|
|
142
|
+
overall: {
|
|
143
|
+
monthlySpendCapUsd: 300,
|
|
144
|
+
},
|
|
145
|
+
runtimes: {
|
|
146
|
+
"claude-code": {
|
|
147
|
+
monthlySpendCapUsd: 150,
|
|
148
|
+
claudeOAuthPlan: "enterprise",
|
|
149
|
+
},
|
|
150
|
+
"openai-codex-app-server": {
|
|
151
|
+
monthlySpendCapUsd: 150,
|
|
145
152
|
},
|
|
146
153
|
},
|
|
147
154
|
});
|
|
@@ -25,22 +25,15 @@ const nullablePositiveNumber = z.preprocess((value) => {
|
|
|
25
25
|
return value;
|
|
26
26
|
}, z.number().finite().positive().nullable());
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
if (value === "" || value == null) return null;
|
|
30
|
-
if (typeof value === "string") return Number(value);
|
|
31
|
-
return value;
|
|
32
|
-
}, z.number().int().positive().nullable());
|
|
28
|
+
export const claudeOAuthPlanSchema = z.enum(["pro", "max_5x", "max_20x"]);
|
|
33
29
|
|
|
34
30
|
export const runtimeBudgetPolicySchema = z.object({
|
|
35
|
-
dailySpendCapUsd: nullablePositiveNumber,
|
|
36
31
|
monthlySpendCapUsd: nullablePositiveNumber,
|
|
37
|
-
|
|
38
|
-
monthlyTokenCap: nullablePositiveInteger,
|
|
32
|
+
claudeOAuthPlan: claudeOAuthPlanSchema.optional(),
|
|
39
33
|
});
|
|
40
34
|
|
|
41
35
|
export const budgetPolicySchema = z.object({
|
|
42
36
|
overall: z.object({
|
|
43
|
-
dailySpendCapUsd: nullablePositiveNumber,
|
|
44
37
|
monthlySpendCapUsd: nullablePositiveNumber,
|
|
45
38
|
}),
|
|
46
39
|
runtimes: z.object(
|
|
@@ -55,3 +48,4 @@ export const updateBudgetPolicySchema = budgetPolicySchema;
|
|
|
55
48
|
export type RuntimeBudgetPolicy = z.infer<typeof runtimeBudgetPolicySchema>;
|
|
56
49
|
export type BudgetPolicy = z.infer<typeof budgetPolicySchema>;
|
|
57
50
|
export type UpdateBudgetPolicyInput = z.infer<typeof updateBudgetPolicySchema>;
|
|
51
|
+
export type ClaudeOAuthPlan = z.infer<typeof claudeOAuthPlanSchema>;
|
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
buildSwarmWorkerPrompt,
|
|
17
17
|
getSwarmWorkflowStructure,
|
|
18
18
|
} from "./swarm";
|
|
19
|
+
import {
|
|
20
|
+
openLearningSession,
|
|
21
|
+
closeLearningSession,
|
|
22
|
+
} from "@/lib/agents/learning-session";
|
|
19
23
|
|
|
20
24
|
/**
|
|
21
25
|
* Execute a workflow by advancing through its steps according to the pattern.
|
|
@@ -43,6 +47,10 @@ export async function executeWorkflow(workflowId: string): Promise<void> {
|
|
|
43
47
|
timestamp: new Date(),
|
|
44
48
|
});
|
|
45
49
|
|
|
50
|
+
// Open a learning session to buffer context proposals during execution.
|
|
51
|
+
// Proposals are collected and presented as a single batch at workflow end.
|
|
52
|
+
openLearningSession(workflowId);
|
|
53
|
+
|
|
46
54
|
// Loop pattern manages its own lifecycle — delegate fully
|
|
47
55
|
if (definition.pattern === "loop") {
|
|
48
56
|
try {
|
|
@@ -72,6 +80,11 @@ export async function executeWorkflow(workflowId: string): Promise<void> {
|
|
|
72
80
|
}),
|
|
73
81
|
timestamp: new Date(),
|
|
74
82
|
});
|
|
83
|
+
} finally {
|
|
84
|
+
// Close learning session — flush buffered proposals as batch notification
|
|
85
|
+
await closeLearningSession(workflowId).catch((err) => {
|
|
86
|
+
console.error("[workflow-engine] Failed to close learning session:", err);
|
|
87
|
+
});
|
|
75
88
|
}
|
|
76
89
|
return;
|
|
77
90
|
}
|
|
@@ -128,6 +141,11 @@ export async function executeWorkflow(workflowId: string): Promise<void> {
|
|
|
128
141
|
}),
|
|
129
142
|
timestamp: new Date(),
|
|
130
143
|
});
|
|
144
|
+
} finally {
|
|
145
|
+
// Close learning session — flush buffered proposals as batch notification
|
|
146
|
+
await closeLearningSession(workflowId).catch((err) => {
|
|
147
|
+
console.error("[workflow-engine] Failed to close learning session:", err);
|
|
148
|
+
});
|
|
131
149
|
}
|
|
132
150
|
}
|
|
133
151
|
|