@workflow-cannon/workspace-kit 0.17.0 → 0.24.0
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 +23 -9
- package/dist/cli/doctor-planning-issues.js +3 -22
- package/dist/cli/run-command.js +22 -38
- package/dist/cli.js +95 -4
- package/dist/contracts/command-manifest.d.ts +17 -0
- package/dist/contracts/command-manifest.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/module-contract.d.ts +12 -11
- package/dist/core/agent-instruction-surface.d.ts +33 -0
- package/dist/core/agent-instruction-surface.js +46 -0
- package/dist/core/config-cli.js +13 -17
- package/dist/core/config-metadata.js +101 -2
- package/dist/core/index.d.ts +4 -1
- package/dist/core/index.js +3 -0
- package/dist/core/module-command-router.js +19 -1
- package/dist/core/module-registry-resolve.d.ts +27 -0
- package/dist/core/module-registry-resolve.js +91 -0
- package/dist/core/module-registry.d.ts +14 -0
- package/dist/core/module-registry.js +57 -0
- package/dist/core/planning/build-plan-session-file.d.ts +29 -0
- package/dist/core/planning/build-plan-session-file.js +58 -0
- package/dist/core/planning/index.d.ts +17 -0
- package/dist/core/planning/index.js +15 -0
- package/dist/core/policy.js +18 -8
- package/dist/core/state/unified-state-db.d.ts +21 -0
- package/dist/core/state/unified-state-db.js +80 -0
- package/dist/core/workspace-kit-config.js +13 -1
- package/dist/modules/agent-behavior/builtins.d.ts +3 -0
- package/dist/modules/agent-behavior/builtins.js +71 -0
- package/dist/modules/agent-behavior/explain.d.ts +6 -0
- package/dist/modules/agent-behavior/explain.js +46 -0
- package/dist/modules/agent-behavior/index.d.ts +4 -0
- package/dist/modules/agent-behavior/index.js +461 -0
- package/dist/modules/agent-behavior/interview-session-file.d.ts +9 -0
- package/dist/modules/agent-behavior/interview-session-file.js +43 -0
- package/dist/modules/agent-behavior/interview.d.ts +13 -0
- package/dist/modules/agent-behavior/interview.js +88 -0
- package/dist/modules/agent-behavior/persistence.d.ts +6 -0
- package/dist/modules/agent-behavior/persistence.js +89 -0
- package/dist/modules/agent-behavior/store.d.ts +34 -0
- package/dist/modules/agent-behavior/store.js +119 -0
- package/dist/modules/agent-behavior/types.d.ts +28 -0
- package/dist/modules/agent-behavior/types.js +1 -0
- package/dist/modules/agent-behavior/validate.d.ts +11 -0
- package/dist/modules/agent-behavior/validate.js +123 -0
- package/dist/modules/approvals/index.js +54 -51
- package/dist/modules/approvals/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/approvals/policy-sensitive-commands.js +4 -0
- package/dist/modules/approvals/review-runtime.js +1 -2
- package/dist/modules/documentation/index.js +47 -45
- package/dist/modules/documentation/normalizer.d.ts +3 -0
- package/dist/modules/documentation/normalizer.js +171 -0
- package/dist/modules/documentation/parser.d.ts +7 -0
- package/dist/modules/documentation/parser.js +39 -0
- package/dist/modules/documentation/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/documentation/policy-sensitive-commands.js +8 -0
- package/dist/modules/documentation/renderer.d.ts +23 -0
- package/dist/modules/documentation/renderer.js +105 -0
- package/dist/modules/documentation/runtime-batch.d.ts +10 -0
- package/dist/modules/documentation/runtime-batch.js +67 -0
- package/dist/modules/documentation/runtime-config.d.ts +11 -0
- package/dist/modules/documentation/runtime-config.js +54 -0
- package/dist/modules/documentation/runtime-render-support.d.ts +8 -0
- package/dist/modules/documentation/runtime-render-support.js +36 -0
- package/dist/modules/documentation/runtime.js +22 -510
- package/dist/modules/documentation/types.d.ts +182 -0
- package/dist/modules/documentation/validator.d.ts +8 -0
- package/dist/modules/documentation/validator.js +234 -0
- package/dist/modules/documentation/view-models.d.ts +3 -0
- package/dist/modules/documentation/view-models.js +124 -0
- package/dist/modules/improvement/generate-recommendations-runtime.js +3 -3
- package/dist/modules/improvement/improvement-state.d.ts +2 -2
- package/dist/modules/improvement/improvement-state.js +52 -23
- package/dist/modules/improvement/index.js +140 -138
- package/dist/modules/improvement/ingest.d.ts +1 -1
- package/dist/modules/improvement/policy-sensitive-commands.d.ts +4 -0
- package/dist/modules/improvement/policy-sensitive-commands.js +7 -0
- package/dist/modules/index.d.ts +6 -0
- package/dist/modules/index.js +17 -0
- package/dist/modules/planning/artifact.d.ts +19 -0
- package/dist/modules/planning/artifact.js +72 -0
- package/dist/modules/planning/index.js +605 -6
- package/dist/modules/planning/question-engine.d.ts +25 -0
- package/dist/modules/planning/question-engine.js +284 -0
- package/dist/modules/planning/types.d.ts +9 -0
- package/dist/modules/planning/types.js +39 -0
- package/dist/modules/task-engine/doctor-planning-persistence.js +21 -13
- package/dist/modules/task-engine/index.d.ts +1 -2
- package/dist/modules/task-engine/index.js +1 -1143
- package/dist/modules/task-engine/migrate-task-persistence-runtime.js +31 -4
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.d.ts +2 -0
- package/dist/modules/task-engine/migrate-wishlist-intake-runtime.js +146 -0
- package/dist/modules/task-engine/planning-open.d.ts +2 -9
- package/dist/modules/task-engine/planning-open.js +4 -15
- package/dist/modules/task-engine/policy-sensitive-commands.d.ts +5 -0
- package/dist/modules/task-engine/policy-sensitive-commands.js +5 -0
- package/dist/modules/task-engine/sqlite-dual-planning.d.ts +11 -2
- package/dist/modules/task-engine/sqlite-dual-planning.js +134 -28
- package/dist/modules/task-engine/strict-task-validation.js +3 -0
- package/dist/modules/task-engine/suggestions.js +2 -1
- package/dist/modules/task-engine/task-engine-internal.d.ts +2 -0
- package/dist/modules/task-engine/task-engine-internal.js +1304 -0
- package/dist/modules/task-engine/task-type-validation.js +40 -0
- package/dist/modules/task-engine/wishlist-intake.d.ts +22 -0
- package/dist/modules/task-engine/wishlist-intake.js +180 -0
- package/dist/modules/task-engine/wishlist-validation.d.ts +4 -0
- package/dist/modules/task-engine/wishlist-validation.js +19 -0
- package/dist/modules/workspace-config/index.js +9 -11
- package/package.json +2 -2
- package/schemas/agent-behavior-profile.schema.json +52 -0
- package/schemas/task-engine-run-contracts.schema.json +80 -5
- package/src/modules/documentation/README.md +16 -25
- package/src/modules/documentation/RULES.md +9 -9
- package/src/modules/documentation/index.ts +54 -49
- package/src/modules/documentation/instructions/document-project.md +6 -6
- package/src/modules/documentation/instructions/generate-document.md +4 -4
- package/src/modules/documentation/normalizer.ts +187 -0
- package/src/modules/documentation/parser.ts +41 -0
- package/src/modules/documentation/policy-sensitive-commands.ts +8 -0
- package/src/modules/documentation/renderer.ts +121 -0
- package/src/modules/documentation/runtime-batch.ts +74 -0
- package/src/modules/documentation/runtime-config.ts +68 -0
- package/src/modules/documentation/runtime-render-support.ts +39 -0
- package/src/modules/documentation/runtime.ts +28 -600
- package/src/modules/documentation/schemas/documentation-schema.md +37 -54
- package/src/modules/documentation/types.ts +228 -0
- package/src/modules/documentation/validator.ts +247 -0
- package/src/modules/documentation/view-models.ts +132 -0
- package/src/modules/documentation/views/agents.view.yaml +18 -0
- package/src/modules/documentation/views/architecture.view.yaml +18 -0
- package/src/modules/documentation/views/principles.view.yaml +18 -0
- package/src/modules/documentation/views/readme.view.yaml +18 -0
- package/src/modules/documentation/views/releasing.view.yaml +18 -0
- package/src/modules/documentation/views/roadmap.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-consumer-cadence.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-parity-validation-flow.view.yaml +18 -0
- package/src/modules/documentation/views/runbooks-release-channels.view.yaml +18 -0
- package/src/modules/documentation/views/security.view.yaml +18 -0
- package/src/modules/documentation/views/support.view.yaml +18 -0
- package/src/modules/documentation/views/terms.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-phase2-config-policy-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-task-engine-workbook.view.yaml +18 -0
- package/src/modules/documentation/views/workbooks-transcript-automation-baseline.view.yaml +18 -0
- package/src/modules/documentation/state.md +0 -8
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
export { BUILTIN_PROFILES, DEFAULT_BUILTIN_PROFILE_ID } from "./builtins.js";
|
|
2
|
+
export { validateBehaviorProfile, mergeDimensions } from "./validate.js";
|
|
3
|
+
import { BUILTIN_PROFILES, DEFAULT_BUILTIN_PROFILE_ID } from "./builtins.js";
|
|
4
|
+
import { diffProfiles, summarizeProfileMarkdown } from "./explain.js";
|
|
5
|
+
import { buildDraftProfileFromInterview, INTERVIEW_QUESTIONS, dimensionsFromAnswers } from "./interview.js";
|
|
6
|
+
import { clearBehaviorInterviewSession, persistBehaviorInterviewSession, readBehaviorInterviewSession } from "./interview-session-file.js";
|
|
7
|
+
import { loadBehaviorWorkspaceState, saveBehaviorWorkspaceState } from "./persistence.js";
|
|
8
|
+
import { BehaviorProfileStore, materializeCustomFromBase } from "./store.js";
|
|
9
|
+
import { mergeDimensions, validateBehaviorProfile } from "./validate.js";
|
|
10
|
+
async function withStore(ctx, fn) {
|
|
11
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
12
|
+
const store = new BehaviorProfileStore(raw);
|
|
13
|
+
const result = await fn(store);
|
|
14
|
+
if (result.ok) {
|
|
15
|
+
await saveBehaviorWorkspaceState(ctx, store.getState());
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
export const agentBehaviorModule = {
|
|
20
|
+
registration: {
|
|
21
|
+
id: "agent-behavior",
|
|
22
|
+
version: "0.1.0",
|
|
23
|
+
contractVersion: "1",
|
|
24
|
+
stateSchema: 1,
|
|
25
|
+
capabilities: ["agent-behavior"],
|
|
26
|
+
dependsOn: [],
|
|
27
|
+
optionalPeers: [],
|
|
28
|
+
enabledByDefault: true,
|
|
29
|
+
config: {
|
|
30
|
+
path: "src/modules/agent-behavior/config.md",
|
|
31
|
+
format: "md",
|
|
32
|
+
description: "Advisory agent interaction profiles (soft layer; subordinate to PRINCIPLES and policy)."
|
|
33
|
+
},
|
|
34
|
+
instructions: {
|
|
35
|
+
directory: "src/modules/agent-behavior/instructions",
|
|
36
|
+
entries: [
|
|
37
|
+
{ name: "list-behavior-profiles", file: "list-behavior-profiles.md", description: "List builtin and custom behavior profile ids." },
|
|
38
|
+
{ name: "get-behavior-profile", file: "get-behavior-profile.md", description: "Get one behavior profile by id (resolved)." },
|
|
39
|
+
{ name: "resolve-behavior-profile", file: "resolve-behavior-profile.md", description: "Effective profile + provenance for the workspace." },
|
|
40
|
+
{ name: "set-active-behavior-profile", file: "set-active-behavior-profile.md", description: "Set or clear the workspace active behavior profile." },
|
|
41
|
+
{ name: "create-behavior-profile", file: "create-behavior-profile.md", description: "Create a custom profile, optionally forked from a base." },
|
|
42
|
+
{ name: "update-behavior-profile", file: "update-behavior-profile.md", description: "Patch a custom behavior profile." },
|
|
43
|
+
{ name: "delete-behavior-profile", file: "delete-behavior-profile.md", description: "Delete a custom behavior profile." },
|
|
44
|
+
{ name: "diff-behavior-profiles", file: "diff-behavior-profiles.md", description: "Structured diff of two profiles." },
|
|
45
|
+
{ name: "explain-behavior-profiles", file: "explain-behavior-profiles.md", description: "Deterministic markdown summary or compare." },
|
|
46
|
+
{ name: "interview-behavior-profile", file: "interview-behavior-profile.md", description: "Guided interview to draft a custom profile." }
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async onCommand(command, ctx) {
|
|
51
|
+
const args = command.args ?? {};
|
|
52
|
+
const name = command.name;
|
|
53
|
+
if (name === "list-behavior-profiles") {
|
|
54
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
55
|
+
const store = new BehaviorProfileStore(raw);
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
code: "behavior-profiles-listed",
|
|
59
|
+
data: { profiles: store.listIds(), scope: "agent-behavior" }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (name === "get-behavior-profile") {
|
|
63
|
+
const profileId = typeof args.profileId === "string" ? args.profileId.trim() : "";
|
|
64
|
+
if (!profileId) {
|
|
65
|
+
return { ok: false, code: "invalid-args", message: "get-behavior-profile requires profileId" };
|
|
66
|
+
}
|
|
67
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
68
|
+
const store = new BehaviorProfileStore(raw);
|
|
69
|
+
const resolved = store.resolveProfile(profileId);
|
|
70
|
+
if (!resolved) {
|
|
71
|
+
return { ok: false, code: "profile-not-found", message: `Profile '${profileId}' not found` };
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
ok: true,
|
|
75
|
+
code: "behavior-profile-retrieved",
|
|
76
|
+
data: { profile: resolved }
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (name === "resolve-behavior-profile") {
|
|
80
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
81
|
+
const store = new BehaviorProfileStore(raw);
|
|
82
|
+
const { effective, provenance } = store.resolveEffectiveWithProvenance();
|
|
83
|
+
return {
|
|
84
|
+
ok: true,
|
|
85
|
+
code: "behavior-profile-resolved",
|
|
86
|
+
data: {
|
|
87
|
+
effective,
|
|
88
|
+
provenance,
|
|
89
|
+
schemaVersion: 1
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (name === "set-active-behavior-profile") {
|
|
94
|
+
return withStore(ctx, async (store) => {
|
|
95
|
+
const clear = args.clear === true;
|
|
96
|
+
const profileId = typeof args.profileId === "string" ? args.profileId.trim() : "";
|
|
97
|
+
if (!clear && !profileId) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
code: "invalid-args",
|
|
101
|
+
message: "Provide profileId or clear:true"
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (clear) {
|
|
105
|
+
store.setActiveProfileId(null);
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
code: "behavior-active-cleared",
|
|
109
|
+
message: "Active behavior profile cleared (fallback to default)",
|
|
110
|
+
data: { activeProfileId: null }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const resolved = store.resolveProfile(profileId);
|
|
114
|
+
if (!resolved) {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
code: "profile-not-found",
|
|
118
|
+
message: `Profile '${profileId}' not found`
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
store.setActiveProfileId(profileId);
|
|
122
|
+
return {
|
|
123
|
+
ok: true,
|
|
124
|
+
code: "behavior-active-set",
|
|
125
|
+
message: `Active behavior profile set to '${profileId}'`,
|
|
126
|
+
data: { activeProfileId: profileId }
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (name === "create-behavior-profile") {
|
|
131
|
+
return withStore(ctx, async (store) => {
|
|
132
|
+
const newId = typeof args.id === "string" ? args.id.trim() : "";
|
|
133
|
+
if (!newId.startsWith("custom:") || newId.length < 9) {
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
code: "invalid-args",
|
|
137
|
+
message: "create-behavior-profile requires id custom:<slug>"
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
if (store.getRawProfile(newId)) {
|
|
141
|
+
return { ok: false, code: "duplicate-profile-id", message: `Profile '${newId}' already exists` };
|
|
142
|
+
}
|
|
143
|
+
const baseId = typeof args.baseProfileId === "string" && args.baseProfileId.trim().length > 0
|
|
144
|
+
? args.baseProfileId.trim()
|
|
145
|
+
: DEFAULT_BUILTIN_PROFILE_ID;
|
|
146
|
+
const dims = typeof args.dimensions === "object" && args.dimensions !== null && !Array.isArray(args.dimensions)
|
|
147
|
+
? args.dimensions
|
|
148
|
+
: undefined;
|
|
149
|
+
const partialDims = dims
|
|
150
|
+
? {
|
|
151
|
+
deliberationDepth: dims.deliberationDepth,
|
|
152
|
+
changeAppetite: dims.changeAppetite,
|
|
153
|
+
checkInFrequency: dims.checkInFrequency,
|
|
154
|
+
explanationVerbosity: dims.explanationVerbosity,
|
|
155
|
+
explorationStyle: dims.explorationStyle,
|
|
156
|
+
ambiguityHandling: dims.ambiguityHandling
|
|
157
|
+
}
|
|
158
|
+
: undefined;
|
|
159
|
+
const mat = materializeCustomFromBase(baseId, store, newId, {
|
|
160
|
+
label: typeof args.label === "string" ? args.label : undefined,
|
|
161
|
+
summary: typeof args.summary === "string" ? args.summary : undefined,
|
|
162
|
+
dimensions: partialDims,
|
|
163
|
+
interactionNotes: typeof args.interactionNotes === "string" ? args.interactionNotes : undefined
|
|
164
|
+
});
|
|
165
|
+
if (!mat.ok) {
|
|
166
|
+
return { ok: false, code: "invalid-profile", message: mat.message };
|
|
167
|
+
}
|
|
168
|
+
store.putCustomProfile(mat.profile);
|
|
169
|
+
return {
|
|
170
|
+
ok: true,
|
|
171
|
+
code: "behavior-profile-created",
|
|
172
|
+
data: { profile: mat.profile }
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (name === "update-behavior-profile") {
|
|
177
|
+
return withStore(ctx, async (store) => {
|
|
178
|
+
const profileId = typeof args.profileId === "string" ? args.profileId.trim() : "";
|
|
179
|
+
if (!profileId.startsWith("custom:")) {
|
|
180
|
+
return { ok: false, code: "invalid-args", message: "update-behavior-profile requires custom profileId" };
|
|
181
|
+
}
|
|
182
|
+
const existing = store.getRawProfile(profileId);
|
|
183
|
+
if (!existing || BUILTIN_PROFILES[profileId]) {
|
|
184
|
+
return { ok: false, code: "profile-not-found", message: `Custom profile '${profileId}' not found` };
|
|
185
|
+
}
|
|
186
|
+
const updates = typeof args.updates === "object" && args.updates !== null ? args.updates : {};
|
|
187
|
+
const next = { ...existing };
|
|
188
|
+
if (typeof updates.label === "string")
|
|
189
|
+
next.label = updates.label.trim();
|
|
190
|
+
if (typeof updates.summary === "string")
|
|
191
|
+
next.summary = updates.summary.trim();
|
|
192
|
+
if (updates.dimensions && typeof updates.dimensions === "object" && !Array.isArray(updates.dimensions)) {
|
|
193
|
+
next.dimensions = mergeDimensions(existing.dimensions, updates.dimensions);
|
|
194
|
+
}
|
|
195
|
+
if (updates.interactionNotes !== undefined) {
|
|
196
|
+
next.interactionNotes =
|
|
197
|
+
typeof updates.interactionNotes === "string" ? updates.interactionNotes.trim() : undefined;
|
|
198
|
+
}
|
|
199
|
+
next.metadata = {
|
|
200
|
+
...(existing.metadata ?? {}),
|
|
201
|
+
updatedAt: new Date().toISOString()
|
|
202
|
+
};
|
|
203
|
+
const v = validateBehaviorProfile(next);
|
|
204
|
+
if (!v.ok) {
|
|
205
|
+
return { ok: false, code: "invalid-profile", message: v.message };
|
|
206
|
+
}
|
|
207
|
+
store.putCustomProfile(v.profile);
|
|
208
|
+
return { ok: true, code: "behavior-profile-updated", data: { profile: v.profile } };
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (name === "delete-behavior-profile") {
|
|
212
|
+
return withStore(ctx, async (store) => {
|
|
213
|
+
const profileId = typeof args.profileId === "string" ? args.profileId.trim() : "";
|
|
214
|
+
if (!profileId.startsWith("custom:")) {
|
|
215
|
+
return { ok: false, code: "invalid-args", message: "delete-behavior-profile requires custom profileId" };
|
|
216
|
+
}
|
|
217
|
+
if (BUILTIN_PROFILES[profileId]) {
|
|
218
|
+
return { ok: false, code: "invalid-args", message: "Cannot delete builtin profile" };
|
|
219
|
+
}
|
|
220
|
+
if (!store.getRawProfile(profileId)) {
|
|
221
|
+
return { ok: false, code: "profile-not-found", message: `Profile '${profileId}' not found` };
|
|
222
|
+
}
|
|
223
|
+
if (store.getActiveProfileId() === profileId) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
code: "profile-active",
|
|
227
|
+
message: `Profile '${profileId}' is active; clear or change active before delete`
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
store.deleteCustomProfile(profileId);
|
|
231
|
+
return { ok: true, code: "behavior-profile-deleted", data: { profileId } };
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (name === "diff-behavior-profiles") {
|
|
235
|
+
const a = typeof args.profileIdA === "string" ? args.profileIdA.trim() : "";
|
|
236
|
+
const b = typeof args.profileIdB === "string" ? args.profileIdB.trim() : "";
|
|
237
|
+
if (!a || !b) {
|
|
238
|
+
return { ok: false, code: "invalid-args", message: "diff-behavior-profiles requires profileIdA and profileIdB" };
|
|
239
|
+
}
|
|
240
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
241
|
+
const store = new BehaviorProfileStore(raw);
|
|
242
|
+
const pa = store.resolveProfile(a);
|
|
243
|
+
const pb = store.resolveProfile(b);
|
|
244
|
+
if (!pa || !pb) {
|
|
245
|
+
return { ok: false, code: "profile-not-found", message: "One or both profiles not found" };
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
ok: true,
|
|
249
|
+
code: "behavior-profiles-diffed",
|
|
250
|
+
data: { diff: diffProfiles(pa, pb), profileIdA: a, profileIdB: b }
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (name === "explain-behavior-profiles") {
|
|
254
|
+
const mode = typeof args.mode === "string" ? args.mode : "summarize";
|
|
255
|
+
const raw = await loadBehaviorWorkspaceState(ctx);
|
|
256
|
+
const store = new BehaviorProfileStore(raw);
|
|
257
|
+
if (mode === "compare") {
|
|
258
|
+
const ids = Array.isArray(args.profileIds) ? args.profileIds.filter((x) => typeof x === "string") : [];
|
|
259
|
+
if (ids.length < 2) {
|
|
260
|
+
return {
|
|
261
|
+
ok: false,
|
|
262
|
+
code: "invalid-args",
|
|
263
|
+
message: "compare mode requires profileIds array with at least two ids"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const sections = [];
|
|
267
|
+
for (const id of ids) {
|
|
268
|
+
const p = store.resolveProfile(id);
|
|
269
|
+
if (!p) {
|
|
270
|
+
return { ok: false, code: "profile-not-found", message: `Profile '${id}' not found` };
|
|
271
|
+
}
|
|
272
|
+
sections.push(summarizeProfileMarkdown(p));
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
ok: true,
|
|
276
|
+
code: "behavior-profiles-explained",
|
|
277
|
+
data: { mode: "compare", markdown: sections.join("\n\n---\n\n") }
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const profileId = typeof args.profileId === "string" && args.profileId.trim().length > 0
|
|
281
|
+
? args.profileId.trim()
|
|
282
|
+
: DEFAULT_BUILTIN_PROFILE_ID;
|
|
283
|
+
const p = store.resolveProfile(profileId);
|
|
284
|
+
if (!p) {
|
|
285
|
+
return { ok: false, code: "profile-not-found", message: `Profile '${profileId}' not found` };
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
ok: true,
|
|
289
|
+
code: "behavior-profiles-explained",
|
|
290
|
+
data: { mode: "summarize", markdown: summarizeProfileMarkdown(p) }
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (name === "interview-behavior-profile") {
|
|
294
|
+
const action = typeof args.action === "string" ? args.action : "";
|
|
295
|
+
const ws = ctx.workspacePath;
|
|
296
|
+
if (action === "discard") {
|
|
297
|
+
await clearBehaviorInterviewSession(ws);
|
|
298
|
+
return { ok: true, code: "behavior-interview-discarded", data: {} };
|
|
299
|
+
}
|
|
300
|
+
if (action === "start") {
|
|
301
|
+
await persistBehaviorInterviewSession(ws, { stepIndex: 0, answers: {} });
|
|
302
|
+
const q = INTERVIEW_QUESTIONS[0];
|
|
303
|
+
return {
|
|
304
|
+
ok: true,
|
|
305
|
+
code: "behavior-interview-started",
|
|
306
|
+
data: {
|
|
307
|
+
stepIndex: 0,
|
|
308
|
+
totalSteps: INTERVIEW_QUESTIONS.length,
|
|
309
|
+
question: q,
|
|
310
|
+
resumeCli: `workspace-kit run interview-behavior-profile '{"action":"answer","value":"<option>"}'`
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
let session = await readBehaviorInterviewSession(ws);
|
|
315
|
+
if (!session && action === "answer") {
|
|
316
|
+
return {
|
|
317
|
+
ok: false,
|
|
318
|
+
code: "invalid-args",
|
|
319
|
+
message: "No interview session; run action:start first"
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (action === "back") {
|
|
323
|
+
if (!session) {
|
|
324
|
+
return { ok: false, code: "invalid-args", message: "No interview session" };
|
|
325
|
+
}
|
|
326
|
+
if (session.stepIndex <= 0) {
|
|
327
|
+
return { ok: true, code: "behavior-interview-back", data: { stepIndex: 0, atStart: true } };
|
|
328
|
+
}
|
|
329
|
+
const prevIndex = session.stepIndex - 1;
|
|
330
|
+
const prevQ = INTERVIEW_QUESTIONS[prevIndex];
|
|
331
|
+
const nextAnswers = { ...session.answers };
|
|
332
|
+
delete nextAnswers[INTERVIEW_QUESTIONS[session.stepIndex - 1].id];
|
|
333
|
+
await persistBehaviorInterviewSession(ws, { stepIndex: prevIndex, answers: nextAnswers });
|
|
334
|
+
return {
|
|
335
|
+
ok: true,
|
|
336
|
+
code: "behavior-interview-back",
|
|
337
|
+
data: {
|
|
338
|
+
stepIndex: prevIndex,
|
|
339
|
+
question: prevQ
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (action === "answer") {
|
|
344
|
+
session = session ?? { schemaVersion: 1, updatedAt: "", stepIndex: 0, answers: {} };
|
|
345
|
+
const value = typeof args.value === "string" ? args.value.trim() : "";
|
|
346
|
+
const q = INTERVIEW_QUESTIONS[session.stepIndex];
|
|
347
|
+
if (!q) {
|
|
348
|
+
return { ok: false, code: "invalid-state", message: "Interview already complete; use finalize" };
|
|
349
|
+
}
|
|
350
|
+
const allowed = new Set(q.options.map((o) => o.value));
|
|
351
|
+
if (!allowed.has(value)) {
|
|
352
|
+
return {
|
|
353
|
+
ok: false,
|
|
354
|
+
code: "invalid-args",
|
|
355
|
+
message: `Invalid value for step ${session.stepIndex}; choose one of: ${[...allowed].join(", ")}`
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const answers = { ...session.answers, [q.id]: value };
|
|
359
|
+
const nextIndex = session.stepIndex + 1;
|
|
360
|
+
await persistBehaviorInterviewSession(ws, { stepIndex: nextIndex, answers });
|
|
361
|
+
if (nextIndex >= INTERVIEW_QUESTIONS.length) {
|
|
362
|
+
return {
|
|
363
|
+
ok: true,
|
|
364
|
+
code: "behavior-interview-complete",
|
|
365
|
+
data: {
|
|
366
|
+
stepIndex: nextIndex,
|
|
367
|
+
complete: true,
|
|
368
|
+
answers,
|
|
369
|
+
next: "Run action:finalize with custom id and optional apply:true"
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const nq = INTERVIEW_QUESTIONS[nextIndex];
|
|
374
|
+
return {
|
|
375
|
+
ok: true,
|
|
376
|
+
code: "behavior-interview-progress",
|
|
377
|
+
data: {
|
|
378
|
+
stepIndex: nextIndex,
|
|
379
|
+
totalSteps: INTERVIEW_QUESTIONS.length,
|
|
380
|
+
question: nq,
|
|
381
|
+
answers
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
if (action === "finalize") {
|
|
386
|
+
session = await readBehaviorInterviewSession(ws);
|
|
387
|
+
if (!session || session.stepIndex < INTERVIEW_QUESTIONS.length) {
|
|
388
|
+
return {
|
|
389
|
+
ok: false,
|
|
390
|
+
code: "invalid-state",
|
|
391
|
+
message: "Interview not complete; answer all questions first"
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
const customId = typeof args.customId === "string" ? args.customId.trim() : "";
|
|
395
|
+
if (!customId.startsWith("custom:")) {
|
|
396
|
+
return {
|
|
397
|
+
ok: false,
|
|
398
|
+
code: "invalid-args",
|
|
399
|
+
message: "finalize requires customId (custom:<slug>)"
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
const dims = dimensionsFromAnswers(session.answers);
|
|
403
|
+
if (!dims) {
|
|
404
|
+
return { ok: false, code: "invalid-state", message: "Missing answers" };
|
|
405
|
+
}
|
|
406
|
+
let built;
|
|
407
|
+
try {
|
|
408
|
+
built = buildDraftProfileFromInterview(session.answers, customId, typeof args.label === "string" ? args.label : undefined);
|
|
409
|
+
}
|
|
410
|
+
catch (e) {
|
|
411
|
+
return { ok: false, code: "invalid-profile", message: e.message };
|
|
412
|
+
}
|
|
413
|
+
const vr = validateBehaviorProfile(built);
|
|
414
|
+
if (!vr.ok) {
|
|
415
|
+
return { ok: false, code: "invalid-profile", message: vr.message };
|
|
416
|
+
}
|
|
417
|
+
const draft = vr.profile;
|
|
418
|
+
const apply = args.apply === true;
|
|
419
|
+
if (apply) {
|
|
420
|
+
const persist = await withStore(ctx, async (store) => {
|
|
421
|
+
if (store.getRawProfile(customId)) {
|
|
422
|
+
return {
|
|
423
|
+
ok: false,
|
|
424
|
+
code: "duplicate-profile-id",
|
|
425
|
+
message: `Profile '${customId}' already exists`
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
store.putCustomProfile(draft);
|
|
429
|
+
store.setActiveProfileId(customId);
|
|
430
|
+
return { ok: true, code: "ok", data: { profile: draft, activeProfileId: customId } };
|
|
431
|
+
});
|
|
432
|
+
if (!persist.ok) {
|
|
433
|
+
return persist;
|
|
434
|
+
}
|
|
435
|
+
await clearBehaviorInterviewSession(ws);
|
|
436
|
+
return {
|
|
437
|
+
ok: true,
|
|
438
|
+
code: "behavior-interview-finalized",
|
|
439
|
+
data: persist.data
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
await clearBehaviorInterviewSession(ws);
|
|
443
|
+
return {
|
|
444
|
+
ok: true,
|
|
445
|
+
code: "behavior-interview-draft",
|
|
446
|
+
data: { profile: draft, apply: false }
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
ok: false,
|
|
451
|
+
code: "invalid-args",
|
|
452
|
+
message: "interview-behavior-profile requires action: start | answer | back | finalize | discard"
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
return {
|
|
456
|
+
ok: false,
|
|
457
|
+
code: "unknown-command",
|
|
458
|
+
message: `agent-behavior: unknown command '${name}'`
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type BehaviorInterviewSessionV1 = {
|
|
2
|
+
schemaVersion: 1;
|
|
3
|
+
updatedAt: string;
|
|
4
|
+
stepIndex: number;
|
|
5
|
+
answers: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
export declare function persistBehaviorInterviewSession(workspacePath: string, snapshot: Omit<BehaviorInterviewSessionV1, "schemaVersion" | "updatedAt">): Promise<void>;
|
|
8
|
+
export declare function clearBehaviorInterviewSession(workspacePath: string): Promise<void>;
|
|
9
|
+
export declare function readBehaviorInterviewSession(workspacePath: string): Promise<BehaviorInterviewSessionV1 | null>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const REL_DIR = path.join(".workspace-kit", "agent-behavior");
|
|
4
|
+
const FILE_NAME = "interview-session.json";
|
|
5
|
+
function sessionPath(workspacePath) {
|
|
6
|
+
return path.join(workspacePath, REL_DIR, FILE_NAME);
|
|
7
|
+
}
|
|
8
|
+
export async function persistBehaviorInterviewSession(workspacePath, snapshot) {
|
|
9
|
+
const dir = path.join(workspacePath, REL_DIR);
|
|
10
|
+
await fs.mkdir(dir, { recursive: true });
|
|
11
|
+
const full = {
|
|
12
|
+
schemaVersion: 1,
|
|
13
|
+
updatedAt: new Date().toISOString(),
|
|
14
|
+
...snapshot
|
|
15
|
+
};
|
|
16
|
+
await fs.writeFile(sessionPath(workspacePath), `${JSON.stringify(full, null, 2)}\n`, "utf8");
|
|
17
|
+
}
|
|
18
|
+
export async function clearBehaviorInterviewSession(workspacePath) {
|
|
19
|
+
try {
|
|
20
|
+
await fs.unlink(sessionPath(workspacePath));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const code = err.code;
|
|
24
|
+
if (code !== "ENOENT")
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function readBehaviorInterviewSession(workspacePath) {
|
|
29
|
+
try {
|
|
30
|
+
const raw = await fs.readFile(sessionPath(workspacePath), "utf8");
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
if (parsed?.schemaVersion !== 1)
|
|
33
|
+
return null;
|
|
34
|
+
if (typeof parsed.stepIndex !== "number")
|
|
35
|
+
return null;
|
|
36
|
+
if (!parsed.answers || typeof parsed.answers !== "object")
|
|
37
|
+
return null;
|
|
38
|
+
return parsed;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BehaviorDimensions, BehaviorProfile } from "./types.js";
|
|
2
|
+
export declare const INTERVIEW_QUESTIONS: {
|
|
3
|
+
id: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
options: {
|
|
6
|
+
value: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}[];
|
|
9
|
+
}[];
|
|
10
|
+
export declare function dimensionsFromAnswers(answers: Record<string, string>): BehaviorDimensions | null;
|
|
11
|
+
export declare function buildDraftProfileFromInterview(answers: Record<string, string>, customId: string, label?: string): Omit<BehaviorProfile, "metadata"> & {
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { BEHAVIOR_PROFILE_SCHEMA_VERSION } from "./types.js";
|
|
2
|
+
export const INTERVIEW_QUESTIONS = [
|
|
3
|
+
{
|
|
4
|
+
id: "changeAppetite",
|
|
5
|
+
prompt: "When suggesting code changes, how aggressive should the agent be?",
|
|
6
|
+
options: [
|
|
7
|
+
{ value: "conservative", label: "Conservative — smallest diffs, extra caution" },
|
|
8
|
+
{ value: "balanced", label: "Balanced — sensible defaults" },
|
|
9
|
+
{ value: "bold", label: "Bold — willing to propose larger refactors when helpful" }
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "deliberationDepth",
|
|
14
|
+
prompt: "How much should the agent think out loud before acting?",
|
|
15
|
+
options: [
|
|
16
|
+
{ value: "low", label: "Low — get to the point" },
|
|
17
|
+
{ value: "medium", label: "Medium — short reasoning" },
|
|
18
|
+
{ value: "high", label: "High — explicit tradeoffs and checks" }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "explanationVerbosity",
|
|
23
|
+
prompt: "How verbose should explanations be?",
|
|
24
|
+
options: [
|
|
25
|
+
{ value: "terse", label: "Terse" },
|
|
26
|
+
{ value: "normal", label: "Normal" },
|
|
27
|
+
{ value: "verbose", label: "Verbose — more context and structure" }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "explorationStyle",
|
|
32
|
+
prompt: "When exploring solutions, prefer:",
|
|
33
|
+
options: [
|
|
34
|
+
{ value: "linear", label: "Linear — one path at a time" },
|
|
35
|
+
{ value: "parallel", label: "Parallel — briefly compare alternatives" }
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "ambiguityHandling",
|
|
40
|
+
prompt: "When requirements are ambiguous:",
|
|
41
|
+
options: [
|
|
42
|
+
{ value: "ask", label: "Ask the user before assuming" },
|
|
43
|
+
{ value: "decide", label: "Make a reasonable assumption and state it" }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "checkInFrequency",
|
|
48
|
+
prompt: "How often should the agent pause for your confirmation on non-policy judgment calls?",
|
|
49
|
+
options: [
|
|
50
|
+
{ value: "rare", label: "Rarely — only when high impact" },
|
|
51
|
+
{ value: "normal", label: "Normal" },
|
|
52
|
+
{ value: "often", label: "Often — prefer checkpoints" }
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
export function dimensionsFromAnswers(answers) {
|
|
57
|
+
const keys = [
|
|
58
|
+
"changeAppetite",
|
|
59
|
+
"deliberationDepth",
|
|
60
|
+
"explanationVerbosity",
|
|
61
|
+
"explorationStyle",
|
|
62
|
+
"ambiguityHandling",
|
|
63
|
+
"checkInFrequency"
|
|
64
|
+
];
|
|
65
|
+
const out = {};
|
|
66
|
+
for (const k of keys) {
|
|
67
|
+
const v = answers[k];
|
|
68
|
+
if (typeof v !== "string" || !v.length) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
out[k] = v;
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
export function buildDraftProfileFromInterview(answers, customId, label) {
|
|
76
|
+
const dimensions = dimensionsFromAnswers(answers);
|
|
77
|
+
if (!dimensions) {
|
|
78
|
+
throw new Error("Incomplete interview answers");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
schemaVersion: BEHAVIOR_PROFILE_SCHEMA_VERSION,
|
|
82
|
+
id: customId,
|
|
83
|
+
label: label?.trim() || "Interview profile",
|
|
84
|
+
summary: "Custom profile produced from interview-behavior-profile.",
|
|
85
|
+
dimensions,
|
|
86
|
+
metadata: { source: "interview", createdAt: new Date().toISOString() }
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModuleLifecycleContext } from "../../contracts/module-contract.js";
|
|
2
|
+
import type { BehaviorWorkspaceStateV1 } from "./types.js";
|
|
3
|
+
export declare function behaviorPersistenceMode(effectiveConfig: Record<string, unknown> | undefined): "json" | "sqlite";
|
|
4
|
+
export declare function behaviorSqliteRelativePath(effectiveConfig: Record<string, unknown> | undefined): string;
|
|
5
|
+
export declare function loadBehaviorWorkspaceState(ctx: ModuleLifecycleContext): Promise<BehaviorWorkspaceStateV1>;
|
|
6
|
+
export declare function saveBehaviorWorkspaceState(ctx: ModuleLifecycleContext, state: BehaviorWorkspaceStateV1): Promise<void>;
|