@workflow-cannon/workspace-kit 0.6.0 → 0.8.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 +3 -3
- package/dist/cli.js +31 -21
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/module-contract.d.ts +13 -0
- package/dist/core/config-metadata.js +303 -1
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.js +6 -0
- package/dist/core/instruction-template-mapper.d.ts +9 -0
- package/dist/core/instruction-template-mapper.js +35 -0
- package/dist/core/lineage-contract.d.ts +1 -1
- package/dist/core/lineage-contract.js +1 -1
- package/dist/core/policy.d.ts +4 -1
- package/dist/core/policy.js +5 -4
- package/dist/core/response-template-contract.d.ts +15 -0
- package/dist/core/response-template-contract.js +10 -0
- package/dist/core/response-template-registry.d.ts +4 -0
- package/dist/core/response-template-registry.js +44 -0
- package/dist/core/response-template-shaping.d.ts +6 -0
- package/dist/core/response-template-shaping.js +128 -0
- package/dist/core/session-policy.d.ts +18 -0
- package/dist/core/session-policy.js +57 -0
- package/dist/core/transcript-completion-hook.d.ts +7 -0
- package/dist/core/transcript-completion-hook.js +90 -0
- package/dist/core/workspace-kit-config.js +42 -2
- package/dist/modules/documentation/runtime.js +383 -14
- package/dist/modules/improvement/generate-recommendations-runtime.d.ts +7 -0
- package/dist/modules/improvement/generate-recommendations-runtime.js +51 -7
- package/dist/modules/improvement/improvement-state.d.ts +12 -1
- package/dist/modules/improvement/improvement-state.js +38 -7
- package/dist/modules/improvement/index.js +124 -2
- package/dist/modules/improvement/ingest.js +2 -1
- package/dist/modules/improvement/transcript-redaction.d.ts +4 -0
- package/dist/modules/improvement/transcript-redaction.js +10 -0
- package/dist/modules/improvement/transcript-sync-runtime.d.ts +60 -0
- package/dist/modules/improvement/transcript-sync-runtime.js +320 -0
- package/dist/modules/index.d.ts +1 -1
- package/dist/modules/index.js +1 -1
- package/dist/modules/task-engine/index.d.ts +0 -2
- package/dist/modules/task-engine/index.js +4 -70
- package/package.json +6 -2
- package/dist/modules/task-engine/generator.d.ts +0 -2
- package/dist/modules/task-engine/generator.js +0 -101
- package/dist/modules/task-engine/importer.d.ts +0 -8
- package/dist/modules/task-engine/importer.js +0 -157
package/README.md
CHANGED
|
@@ -62,7 +62,7 @@ This keeps automation adaptive without sacrificing safety, governance, or develo
|
|
|
62
62
|
## Current Status
|
|
63
63
|
|
|
64
64
|
- **Phase 0** and **Phase 1** (task engine, `v0.3.0`) are complete.
|
|
65
|
-
- **Phase 2** (layered config, policy gates, cutover docs, `v0.4.0`) is complete in-repo; see
|
|
65
|
+
- **Phase 2** (layered config, policy gates, cutover docs, `v0.4.0`) is complete in-repo; see `.workspace-kit/tasks/state.json` and `docs/maintainers/ROADMAP.md`.
|
|
66
66
|
- **Phase 2b** (policy + config UX, `v0.4.1`) and **Phase 3** (enhancement loop MVP, `v0.5.0`) are complete in-repo: evidence-driven **improvement** tasks, **`approvals`** (`review-item`), heuristic confidence, and append-only lineage.
|
|
67
67
|
- **Phase 4** (`v0.6.0`) is complete in-repo: compatibility matrix/gates, diagnostics/SLO baseline evidence, release-channel mapping, and planning-doc consistency checks.
|
|
68
68
|
|
|
@@ -87,7 +87,7 @@ npm install @workflow-cannon/workspace-kit
|
|
|
87
87
|
- `README.md` - project entry point
|
|
88
88
|
- `.ai/PRINCIPLES.md` - project goals and decision principles (canonical AI)
|
|
89
89
|
- `docs/maintainers/ROADMAP.md` - roadmap and decision log
|
|
90
|
-
-
|
|
90
|
+
- `.workspace-kit/tasks/state.json` - execution tracking
|
|
91
91
|
- `docs/maintainers/ARCHITECTURE.md` - architecture direction
|
|
92
92
|
- `docs/maintainers/DECISIONS.md` - focused design/decision notes
|
|
93
93
|
- `docs/maintainers/RELEASING.md` - release checklist and validation expectations
|
|
@@ -100,7 +100,7 @@ npm install @workflow-cannon/workspace-kit
|
|
|
100
100
|
|
|
101
101
|
- Project goals and decision principles: `.ai/PRINCIPLES.md`
|
|
102
102
|
- Strategy and long-range direction: `docs/maintainers/ROADMAP.md`
|
|
103
|
-
- Active execution tasks:
|
|
103
|
+
- Active execution tasks: `.workspace-kit/tasks/state.json`
|
|
104
104
|
- Glossary and agent-guidance terms: `docs/maintainers/TERMS.md`
|
|
105
105
|
- Architecture direction: `docs/maintainers/ARCHITECTURE.md`
|
|
106
106
|
- Project decisions: `docs/maintainers/DECISIONS.md`
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,8 @@ import { pathToFileURL } from "node:url";
|
|
|
5
5
|
import { ModuleRegistry } from "./core/module-registry.js";
|
|
6
6
|
import { ModuleCommandRouter } from "./core/module-command-router.js";
|
|
7
7
|
import { appendPolicyTrace, isSensitiveModuleCommandForEffective, parsePolicyApproval, parsePolicyApprovalFromEnv, resolveActor, resolvePolicyOperationIdForCommand } from "./core/policy.js";
|
|
8
|
+
import { getSessionGrant, recordSessionGrant, resolveSessionId } from "./core/session-policy.js";
|
|
9
|
+
import { applyResponseTemplateApplication } from "./core/response-template-shaping.js";
|
|
8
10
|
import { runWorkspaceConfigCli } from "./core/config-cli.js";
|
|
9
11
|
import { resolveWorkspaceConfigWithLayers } from "./core/workspace-kit-config.js";
|
|
10
12
|
import { documentationModule } from "./modules/documentation/index.js";
|
|
@@ -639,14 +641,22 @@ export async function runCli(args, options = {}) {
|
|
|
639
641
|
}
|
|
640
642
|
const actor = resolveActor(cwd, commandArgs, process.env);
|
|
641
643
|
const sensitive = isSensitiveModuleCommandForEffective(subcommand, commandArgs, effective);
|
|
644
|
+
const sessionId = resolveSessionId(process.env);
|
|
645
|
+
const policyOp = resolvePolicyOperationIdForCommand(subcommand, effective);
|
|
646
|
+
const explicitPolicyApproval = parsePolicyApproval(commandArgs);
|
|
647
|
+
let resolvedSensitiveApproval = explicitPolicyApproval;
|
|
642
648
|
if (sensitive) {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
649
|
+
if (!resolvedSensitiveApproval && policyOp) {
|
|
650
|
+
const grant = await getSessionGrant(cwd, policyOp, sessionId);
|
|
651
|
+
if (grant) {
|
|
652
|
+
resolvedSensitiveApproval = { confirmed: true, rationale: grant.rationale };
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (!resolvedSensitiveApproval) {
|
|
656
|
+
if (policyOp) {
|
|
647
657
|
await appendPolicyTrace(cwd, {
|
|
648
658
|
timestamp: new Date().toISOString(),
|
|
649
|
-
operationId:
|
|
659
|
+
operationId: policyOp,
|
|
650
660
|
command: `run ${subcommand}`,
|
|
651
661
|
actor,
|
|
652
662
|
allowed: false,
|
|
@@ -656,7 +666,7 @@ export async function runCli(args, options = {}) {
|
|
|
656
666
|
writeLine(JSON.stringify({
|
|
657
667
|
ok: false,
|
|
658
668
|
code: "policy-denied",
|
|
659
|
-
message: 'Sensitive command requires policyApproval in JSON args: {"policyApproval":{"confirmed":true,"rationale":"
|
|
669
|
+
message: 'Sensitive command requires policyApproval in JSON args (or an existing session grant for this operation): {"policyApproval":{"confirmed":true,"rationale":"why","scope":"session"}}'
|
|
660
670
|
}, null, 2));
|
|
661
671
|
return EXIT_VALIDATION_FAILURE;
|
|
662
672
|
}
|
|
@@ -669,23 +679,23 @@ export async function runCli(args, options = {}) {
|
|
|
669
679
|
moduleRegistry: registry
|
|
670
680
|
};
|
|
671
681
|
try {
|
|
672
|
-
const
|
|
673
|
-
if (sensitive) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
});
|
|
682
|
+
const rawResult = await router.execute(subcommand, commandArgs, ctx);
|
|
683
|
+
if (sensitive && resolvedSensitiveApproval && policyOp) {
|
|
684
|
+
await appendPolicyTrace(cwd, {
|
|
685
|
+
timestamp: new Date().toISOString(),
|
|
686
|
+
operationId: policyOp,
|
|
687
|
+
command: `run ${subcommand}`,
|
|
688
|
+
actor,
|
|
689
|
+
allowed: true,
|
|
690
|
+
rationale: resolvedSensitiveApproval.rationale,
|
|
691
|
+
commandOk: rawResult.ok,
|
|
692
|
+
message: rawResult.message
|
|
693
|
+
});
|
|
694
|
+
if (explicitPolicyApproval?.scope === "session" && rawResult.ok) {
|
|
695
|
+
await recordSessionGrant(cwd, policyOp, sessionId, explicitPolicyApproval.rationale);
|
|
687
696
|
}
|
|
688
697
|
}
|
|
698
|
+
const result = applyResponseTemplateApplication(subcommand, commandArgs, rawResult, effective);
|
|
689
699
|
writeLine(JSON.stringify(result, null, 2));
|
|
690
700
|
return result.ok ? EXIT_SUCCESS : EXIT_VALIDATION_FAILURE;
|
|
691
701
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type { ConfigRegistryView, ModuleCommand, ModuleCommandResult, ModuleCapability, ModuleDocumentContract, ModuleEvent, ModuleInstructionContract, ModuleInstructionEntry, ModuleLifecycleContext, ModuleRegistration, WorkflowModule } from "./module-contract.js";
|
|
1
|
+
export type { ConfigRegistryView, ResponseTemplateApplicationMeta, ModuleCommand, ModuleCommandResult, ModuleCapability, ModuleDocumentContract, ModuleEvent, ModuleInstructionContract, ModuleInstructionEntry, ModuleLifecycleContext, ModuleRegistration, WorkflowModule } from "./module-contract.js";
|
|
@@ -30,11 +30,24 @@ export type ModuleCommand = {
|
|
|
30
30
|
name: string;
|
|
31
31
|
args?: Record<string, unknown>;
|
|
32
32
|
};
|
|
33
|
+
/** Structured response-template application record (Phase 6b); mirrored in core for shaping. */
|
|
34
|
+
export type ResponseTemplateApplicationMeta = {
|
|
35
|
+
requestedTemplateId: string | null;
|
|
36
|
+
appliedTemplateId: string | null;
|
|
37
|
+
enforcementMode: "advisory" | "strict";
|
|
38
|
+
warnings: string[];
|
|
39
|
+
telemetry?: {
|
|
40
|
+
resolveNs: number;
|
|
41
|
+
warningCount: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
33
44
|
export type ModuleCommandResult = {
|
|
34
45
|
ok: boolean;
|
|
35
46
|
code: string;
|
|
36
47
|
message?: string;
|
|
37
48
|
data?: Record<string, unknown>;
|
|
49
|
+
/** Advisory response-template shaping metadata; always present for `workspace-kit run` JSON output when enabled. */
|
|
50
|
+
responseTemplate?: ResponseTemplateApplicationMeta;
|
|
38
51
|
};
|
|
39
52
|
/** Subset of module registry used for config layer ordering (avoids core↔contracts cycles). */
|
|
40
53
|
export type ConfigRegistryView = {
|
|
@@ -28,6 +28,177 @@ const REGISTRY = {
|
|
|
28
28
|
requiresApproval: true,
|
|
29
29
|
exposure: "maintainer",
|
|
30
30
|
writableLayers: ["project"]
|
|
31
|
+
},
|
|
32
|
+
"improvement.transcripts.sourcePath": {
|
|
33
|
+
key: "improvement.transcripts.sourcePath",
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Relative path to transcript JSONL source files for sync operations.",
|
|
36
|
+
default: ".cursor/agent-transcripts",
|
|
37
|
+
domainScope: "project",
|
|
38
|
+
owningModule: "improvement",
|
|
39
|
+
sensitive: false,
|
|
40
|
+
requiresRestart: false,
|
|
41
|
+
requiresApproval: false,
|
|
42
|
+
exposure: "public",
|
|
43
|
+
writableLayers: ["project", "user"]
|
|
44
|
+
},
|
|
45
|
+
"improvement.transcripts.archivePath": {
|
|
46
|
+
key: "improvement.transcripts.archivePath",
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Relative local archive path where synced transcript JSONL files are copied.",
|
|
49
|
+
default: "agent-transcripts",
|
|
50
|
+
domainScope: "project",
|
|
51
|
+
owningModule: "improvement",
|
|
52
|
+
sensitive: false,
|
|
53
|
+
requiresRestart: false,
|
|
54
|
+
requiresApproval: false,
|
|
55
|
+
exposure: "public",
|
|
56
|
+
writableLayers: ["project", "user"]
|
|
57
|
+
},
|
|
58
|
+
"improvement.cadence.minIntervalMinutes": {
|
|
59
|
+
key: "improvement.cadence.minIntervalMinutes",
|
|
60
|
+
type: "number",
|
|
61
|
+
description: "Minimum minutes between one-shot ingest recommendation generation runs.",
|
|
62
|
+
default: 15,
|
|
63
|
+
domainScope: "project",
|
|
64
|
+
owningModule: "improvement",
|
|
65
|
+
sensitive: false,
|
|
66
|
+
requiresRestart: false,
|
|
67
|
+
requiresApproval: false,
|
|
68
|
+
exposure: "maintainer",
|
|
69
|
+
writableLayers: ["project", "user"]
|
|
70
|
+
},
|
|
71
|
+
"improvement.cadence.skipIfNoNewTranscripts": {
|
|
72
|
+
key: "improvement.cadence.skipIfNoNewTranscripts",
|
|
73
|
+
type: "boolean",
|
|
74
|
+
description: "Skip recommendation generation when transcript sync copies no new files.",
|
|
75
|
+
default: true,
|
|
76
|
+
domainScope: "project",
|
|
77
|
+
owningModule: "improvement",
|
|
78
|
+
sensitive: false,
|
|
79
|
+
requiresRestart: false,
|
|
80
|
+
requiresApproval: false,
|
|
81
|
+
exposure: "maintainer",
|
|
82
|
+
writableLayers: ["project", "user"]
|
|
83
|
+
},
|
|
84
|
+
"improvement.cadence.maxRecommendationCandidatesPerRun": {
|
|
85
|
+
key: "improvement.cadence.maxRecommendationCandidatesPerRun",
|
|
86
|
+
type: "number",
|
|
87
|
+
description: "Upper bound on new improvement tasks created per generate-recommendations run (safety cap; direct runs still respect dedupe).",
|
|
88
|
+
default: 500,
|
|
89
|
+
domainScope: "project",
|
|
90
|
+
owningModule: "improvement",
|
|
91
|
+
sensitive: false,
|
|
92
|
+
requiresRestart: false,
|
|
93
|
+
requiresApproval: false,
|
|
94
|
+
exposure: "maintainer",
|
|
95
|
+
writableLayers: ["project", "user"]
|
|
96
|
+
},
|
|
97
|
+
"improvement.transcripts.maxFilesPerSync": {
|
|
98
|
+
key: "improvement.transcripts.maxFilesPerSync",
|
|
99
|
+
type: "number",
|
|
100
|
+
description: "Maximum JSONL transcript files processed per sync (deterministic order).",
|
|
101
|
+
default: 5000,
|
|
102
|
+
domainScope: "project",
|
|
103
|
+
owningModule: "improvement",
|
|
104
|
+
sensitive: false,
|
|
105
|
+
requiresRestart: false,
|
|
106
|
+
requiresApproval: false,
|
|
107
|
+
exposure: "maintainer",
|
|
108
|
+
writableLayers: ["project", "user"]
|
|
109
|
+
},
|
|
110
|
+
"improvement.transcripts.maxBytesPerFile": {
|
|
111
|
+
key: "improvement.transcripts.maxBytesPerFile",
|
|
112
|
+
type: "number",
|
|
113
|
+
description: "Skip transcript files larger than this many bytes during sync.",
|
|
114
|
+
default: 50_000_000,
|
|
115
|
+
domainScope: "project",
|
|
116
|
+
owningModule: "improvement",
|
|
117
|
+
sensitive: false,
|
|
118
|
+
requiresRestart: false,
|
|
119
|
+
requiresApproval: false,
|
|
120
|
+
exposure: "maintainer",
|
|
121
|
+
writableLayers: ["project", "user"]
|
|
122
|
+
},
|
|
123
|
+
"improvement.transcripts.maxTotalScanBytes": {
|
|
124
|
+
key: "improvement.transcripts.maxTotalScanBytes",
|
|
125
|
+
type: "number",
|
|
126
|
+
description: "Approximate cap on total bytes read for hashing during one sync.",
|
|
127
|
+
default: 500_000_000,
|
|
128
|
+
domainScope: "project",
|
|
129
|
+
owningModule: "improvement",
|
|
130
|
+
sensitive: false,
|
|
131
|
+
requiresRestart: false,
|
|
132
|
+
requiresApproval: false,
|
|
133
|
+
exposure: "maintainer",
|
|
134
|
+
writableLayers: ["project", "user"]
|
|
135
|
+
},
|
|
136
|
+
"improvement.transcripts.discoveryPaths": {
|
|
137
|
+
key: "improvement.transcripts.discoveryPaths",
|
|
138
|
+
type: "array",
|
|
139
|
+
description: "Ordered relative paths tried when improvement.transcripts.sourcePath is unset (first existing wins).",
|
|
140
|
+
default: [],
|
|
141
|
+
domainScope: "project",
|
|
142
|
+
owningModule: "improvement",
|
|
143
|
+
sensitive: false,
|
|
144
|
+
requiresRestart: false,
|
|
145
|
+
requiresApproval: false,
|
|
146
|
+
exposure: "maintainer",
|
|
147
|
+
writableLayers: ["project", "user"]
|
|
148
|
+
},
|
|
149
|
+
"improvement.hooks.afterTaskCompleted": {
|
|
150
|
+
key: "improvement.hooks.afterTaskCompleted",
|
|
151
|
+
type: "string",
|
|
152
|
+
description: "Optional background transcript sync after task-engine transition to completed: off (default), sync, or ingest (ingest requires WORKSPACE_KIT_POLICY_APPROVAL in env).",
|
|
153
|
+
default: "off",
|
|
154
|
+
allowedValues: ["off", "sync", "ingest"],
|
|
155
|
+
domainScope: "project",
|
|
156
|
+
owningModule: "improvement",
|
|
157
|
+
sensitive: false,
|
|
158
|
+
requiresRestart: false,
|
|
159
|
+
requiresApproval: false,
|
|
160
|
+
exposure: "maintainer",
|
|
161
|
+
writableLayers: ["project", "user"]
|
|
162
|
+
},
|
|
163
|
+
"responseTemplates.enforcementMode": {
|
|
164
|
+
key: "responseTemplates.enforcementMode",
|
|
165
|
+
type: "string",
|
|
166
|
+
description: "Whether unknown/mismatched response templates fail commands (`strict`) or only emit warnings (`advisory`).",
|
|
167
|
+
default: "advisory",
|
|
168
|
+
allowedValues: ["advisory", "strict"],
|
|
169
|
+
domainScope: "project",
|
|
170
|
+
owningModule: "workspace-kit",
|
|
171
|
+
sensitive: false,
|
|
172
|
+
requiresRestart: false,
|
|
173
|
+
requiresApproval: false,
|
|
174
|
+
exposure: "maintainer",
|
|
175
|
+
writableLayers: ["project", "user"]
|
|
176
|
+
},
|
|
177
|
+
"responseTemplates.defaultTemplateId": {
|
|
178
|
+
key: "responseTemplates.defaultTemplateId",
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "Builtin response template id applied when a run does not specify one.",
|
|
181
|
+
default: "default",
|
|
182
|
+
domainScope: "project",
|
|
183
|
+
owningModule: "workspace-kit",
|
|
184
|
+
sensitive: false,
|
|
185
|
+
requiresRestart: false,
|
|
186
|
+
requiresApproval: false,
|
|
187
|
+
exposure: "maintainer",
|
|
188
|
+
writableLayers: ["project", "user"]
|
|
189
|
+
},
|
|
190
|
+
"responseTemplates.commandOverrides": {
|
|
191
|
+
key: "responseTemplates.commandOverrides",
|
|
192
|
+
type: "object",
|
|
193
|
+
description: "Map of module command name to builtin response template id.",
|
|
194
|
+
default: {},
|
|
195
|
+
domainScope: "project",
|
|
196
|
+
owningModule: "workspace-kit",
|
|
197
|
+
sensitive: false,
|
|
198
|
+
requiresRestart: false,
|
|
199
|
+
requiresApproval: false,
|
|
200
|
+
exposure: "maintainer",
|
|
201
|
+
writableLayers: ["project", "user"]
|
|
31
202
|
}
|
|
32
203
|
};
|
|
33
204
|
export function getConfigKeyMetadata(key) {
|
|
@@ -68,6 +239,13 @@ export function validateValueForMetadata(meta, value) {
|
|
|
68
239
|
}
|
|
69
240
|
}
|
|
70
241
|
}
|
|
242
|
+
if (meta.key === "improvement.transcripts.discoveryPaths") {
|
|
243
|
+
for (const item of value) {
|
|
244
|
+
if (typeof item !== "string" || item.trim().length === 0) {
|
|
245
|
+
throw new Error(`config-type-error(${meta.key}): array entries must be non-empty strings`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
71
249
|
return;
|
|
72
250
|
}
|
|
73
251
|
if (meta.type === "string" && typeof value !== "string") {
|
|
@@ -83,6 +261,16 @@ export function validateValueForMetadata(meta, value) {
|
|
|
83
261
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
84
262
|
throw typeError(meta.key, "object", value);
|
|
85
263
|
}
|
|
264
|
+
if (meta.key === "responseTemplates.commandOverrides") {
|
|
265
|
+
for (const [k, v] of Object.entries(value)) {
|
|
266
|
+
if (typeof k !== "string" || !k.trim()) {
|
|
267
|
+
throw new Error(`config-type-error(${meta.key}): keys must be non-empty strings`);
|
|
268
|
+
}
|
|
269
|
+
if (typeof v !== "string" || !v.trim()) {
|
|
270
|
+
throw new Error(`config-type-error(${meta.key}): values must be non-empty strings`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
86
274
|
}
|
|
87
275
|
if (meta.allowedValues && meta.allowedValues.length > 0) {
|
|
88
276
|
if (!meta.allowedValues.some((v) => deepEqualLoose(v, value))) {
|
|
@@ -102,7 +290,16 @@ function deepEqualLoose(a, b) {
|
|
|
102
290
|
* Validate top-level shape of persisted kit config files (strict unknown-key rejection).
|
|
103
291
|
*/
|
|
104
292
|
export function validatePersistedConfigDocument(data, label) {
|
|
105
|
-
const allowed = new Set([
|
|
293
|
+
const allowed = new Set([
|
|
294
|
+
"schemaVersion",
|
|
295
|
+
"core",
|
|
296
|
+
"tasks",
|
|
297
|
+
"documentation",
|
|
298
|
+
"policy",
|
|
299
|
+
"improvement",
|
|
300
|
+
"responseTemplates",
|
|
301
|
+
"modules"
|
|
302
|
+
]);
|
|
106
303
|
for (const k of Object.keys(data)) {
|
|
107
304
|
if (!allowed.has(k)) {
|
|
108
305
|
throw new Error(`config-invalid(${label}): unknown top-level key '${k}'`);
|
|
@@ -156,6 +353,111 @@ export function validatePersistedConfigDocument(data, label) {
|
|
|
156
353
|
throw new Error(`config-invalid(${label}): modules must be absent or an empty object`);
|
|
157
354
|
}
|
|
158
355
|
}
|
|
356
|
+
const improvement = data.improvement;
|
|
357
|
+
if (improvement !== undefined) {
|
|
358
|
+
if (typeof improvement !== "object" || improvement === null || Array.isArray(improvement)) {
|
|
359
|
+
throw new Error(`config-invalid(${label}): improvement must be an object`);
|
|
360
|
+
}
|
|
361
|
+
const imp = improvement;
|
|
362
|
+
for (const k of Object.keys(imp)) {
|
|
363
|
+
if (k !== "transcripts" && k !== "cadence" && k !== "hooks") {
|
|
364
|
+
throw new Error(`config-invalid(${label}): unknown improvement.${k}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (imp.transcripts !== undefined) {
|
|
368
|
+
if (typeof imp.transcripts !== "object" ||
|
|
369
|
+
imp.transcripts === null ||
|
|
370
|
+
Array.isArray(imp.transcripts)) {
|
|
371
|
+
throw new Error(`config-invalid(${label}): improvement.transcripts must be an object`);
|
|
372
|
+
}
|
|
373
|
+
const tr = imp.transcripts;
|
|
374
|
+
for (const k of Object.keys(tr)) {
|
|
375
|
+
if (k !== "sourcePath" &&
|
|
376
|
+
k !== "archivePath" &&
|
|
377
|
+
k !== "maxFilesPerSync" &&
|
|
378
|
+
k !== "maxBytesPerFile" &&
|
|
379
|
+
k !== "maxTotalScanBytes" &&
|
|
380
|
+
k !== "discoveryPaths") {
|
|
381
|
+
throw new Error(`config-invalid(${label}): unknown improvement.transcripts.${k}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (tr.sourcePath !== undefined) {
|
|
385
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.sourcePath"], tr.sourcePath);
|
|
386
|
+
}
|
|
387
|
+
if (tr.archivePath !== undefined) {
|
|
388
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.archivePath"], tr.archivePath);
|
|
389
|
+
}
|
|
390
|
+
if (tr.maxFilesPerSync !== undefined) {
|
|
391
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.maxFilesPerSync"], tr.maxFilesPerSync);
|
|
392
|
+
}
|
|
393
|
+
if (tr.maxBytesPerFile !== undefined) {
|
|
394
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.maxBytesPerFile"], tr.maxBytesPerFile);
|
|
395
|
+
}
|
|
396
|
+
if (tr.maxTotalScanBytes !== undefined) {
|
|
397
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.maxTotalScanBytes"], tr.maxTotalScanBytes);
|
|
398
|
+
}
|
|
399
|
+
if (tr.discoveryPaths !== undefined) {
|
|
400
|
+
validateValueForMetadata(REGISTRY["improvement.transcripts.discoveryPaths"], tr.discoveryPaths);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
if (imp.cadence !== undefined) {
|
|
404
|
+
if (typeof imp.cadence !== "object" || imp.cadence === null || Array.isArray(imp.cadence)) {
|
|
405
|
+
throw new Error(`config-invalid(${label}): improvement.cadence must be an object`);
|
|
406
|
+
}
|
|
407
|
+
const cd = imp.cadence;
|
|
408
|
+
for (const k of Object.keys(cd)) {
|
|
409
|
+
if (k !== "minIntervalMinutes" && k !== "skipIfNoNewTranscripts" && k !== "maxRecommendationCandidatesPerRun") {
|
|
410
|
+
throw new Error(`config-invalid(${label}): unknown improvement.cadence.${k}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (cd.minIntervalMinutes !== undefined) {
|
|
414
|
+
validateValueForMetadata(REGISTRY["improvement.cadence.minIntervalMinutes"], cd.minIntervalMinutes);
|
|
415
|
+
}
|
|
416
|
+
if (cd.skipIfNoNewTranscripts !== undefined) {
|
|
417
|
+
validateValueForMetadata(REGISTRY["improvement.cadence.skipIfNoNewTranscripts"], cd.skipIfNoNewTranscripts);
|
|
418
|
+
}
|
|
419
|
+
if (cd.maxRecommendationCandidatesPerRun !== undefined) {
|
|
420
|
+
validateValueForMetadata(REGISTRY["improvement.cadence.maxRecommendationCandidatesPerRun"], cd.maxRecommendationCandidatesPerRun);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (imp.hooks !== undefined) {
|
|
424
|
+
if (typeof imp.hooks !== "object" || imp.hooks === null || Array.isArray(imp.hooks)) {
|
|
425
|
+
throw new Error(`config-invalid(${label}): improvement.hooks must be an object`);
|
|
426
|
+
}
|
|
427
|
+
const hk = imp.hooks;
|
|
428
|
+
for (const k of Object.keys(hk)) {
|
|
429
|
+
if (k !== "afterTaskCompleted") {
|
|
430
|
+
throw new Error(`config-invalid(${label}): unknown improvement.hooks.${k}`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (hk.afterTaskCompleted !== undefined) {
|
|
434
|
+
validateValueForMetadata(REGISTRY["improvement.hooks.afterTaskCompleted"], hk.afterTaskCompleted);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const responseTemplates = data.responseTemplates;
|
|
439
|
+
if (responseTemplates !== undefined) {
|
|
440
|
+
if (typeof responseTemplates !== "object" ||
|
|
441
|
+
responseTemplates === null ||
|
|
442
|
+
Array.isArray(responseTemplates)) {
|
|
443
|
+
throw new Error(`config-invalid(${label}): responseTemplates must be an object`);
|
|
444
|
+
}
|
|
445
|
+
const rt = responseTemplates;
|
|
446
|
+
for (const k of Object.keys(rt)) {
|
|
447
|
+
if (k !== "enforcementMode" && k !== "defaultTemplateId" && k !== "commandOverrides") {
|
|
448
|
+
throw new Error(`config-invalid(${label}): unknown responseTemplates.${k}`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
if (rt.enforcementMode !== undefined) {
|
|
452
|
+
validateValueForMetadata(REGISTRY["responseTemplates.enforcementMode"], rt.enforcementMode);
|
|
453
|
+
}
|
|
454
|
+
if (rt.defaultTemplateId !== undefined) {
|
|
455
|
+
validateValueForMetadata(REGISTRY["responseTemplates.defaultTemplateId"], rt.defaultTemplateId);
|
|
456
|
+
}
|
|
457
|
+
if (rt.commandOverrides !== undefined) {
|
|
458
|
+
validateValueForMetadata(REGISTRY["responseTemplates.commandOverrides"], rt.commandOverrides);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
159
461
|
}
|
|
160
462
|
export function getConfigRegistryExport() {
|
|
161
463
|
return REGISTRY;
|
package/dist/core/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ export { ModuleRegistry, ModuleRegistryError, validateModuleSet, type ModuleRegi
|
|
|
2
2
|
export { ModuleCommandRouter, ModuleCommandRouterError, type ModuleCommandDescriptor, type ModuleCommandRouterOptions } from "./module-command-router.js";
|
|
3
3
|
export { buildBaseConfigLayers, deepMerge, envToConfigOverlay, explainConfigPath, getAtPath, getProjectConfigPath, getUserConfigFilePath, KIT_CONFIG_DEFAULTS, loadUserLayer, mergeConfigLayers, MODULE_CONFIG_CONTRIBUTIONS, normalizeConfigForExport, PROJECT_CONFIG_REL, resolveWorkspaceConfigWithLayers, stableStringifyConfig, type ConfigLayer, type ConfigLayerId, type EffectiveWorkspaceConfig, type ExplainConfigResult, type ResolveWorkspaceConfigOptions } from "./workspace-kit-config.js";
|
|
4
4
|
export { appendPolicyTrace, getExtraSensitiveModuleCommandsFromEffective, getOperationIdForCommand, isSensitiveModuleCommand, isSensitiveModuleCommandForEffective, parsePolicyApproval, parsePolicyApprovalFromEnv, POLICY_TRACE_SCHEMA_VERSION, resolveActor, resolvePolicyOperationIdForCommand, type PolicyOperationId, type PolicyTraceRecord, type PolicyTraceRecordInput } from "./policy.js";
|
|
5
|
+
export { getSessionGrant, loadSessionPolicyDocument, recordSessionGrant, resolveSessionId, SESSION_POLICY_SCHEMA_VERSION, type SessionPolicyDocument, type SessionPolicyGrant } from "./session-policy.js";
|
|
6
|
+
export { parseTemplateDirectiveFromText } from "./instruction-template-mapper.js";
|
|
7
|
+
export { RESPONSE_TEMPLATE_CONTRACT_VERSION, MAX_TEMPLATE_WARNING_LENGTH, truncateTemplateWarning, type ResponseTemplateDefinition, type ResponseTemplateEnforcementMode } from "./response-template-contract.js";
|
|
8
|
+
export { allBuiltinDefinitions, getResponseTemplateDefinition, listBuiltinResponseTemplateIds } from "./response-template-registry.js";
|
|
9
|
+
export { applyResponseTemplateApplication } from "./response-template-shaping.js";
|
|
10
|
+
export { maybeSpawnTranscriptHookAfterCompletion, readAfterTaskCompletedHook, resolveWorkspaceKitCli } from "./transcript-completion-hook.js";
|
|
5
11
|
export { assertWritableKey, getConfigKeyMetadata, getConfigRegistryExport, listConfigMetadata, validatePersistedConfigDocument, validateValueForMetadata, type ConfigKeyExposure, type ConfigKeyMetadata, type ConfigValueType } from "./config-metadata.js";
|
|
6
12
|
export { appendConfigMutation, CONFIG_MUTATIONS_SCHEMA_VERSION, summarizeForEvidence, type ConfigMutationRecord } from "./config-mutations.js";
|
|
7
13
|
export { generateConfigReferenceDocs, runWorkspaceConfigCli, type ConfigCliIo } from "./config-cli.js";
|
package/dist/core/index.js
CHANGED
|
@@ -2,6 +2,12 @@ export { ModuleRegistry, ModuleRegistryError, validateModuleSet } from "./module
|
|
|
2
2
|
export { ModuleCommandRouter, ModuleCommandRouterError } from "./module-command-router.js";
|
|
3
3
|
export { buildBaseConfigLayers, deepMerge, envToConfigOverlay, explainConfigPath, getAtPath, getProjectConfigPath, getUserConfigFilePath, KIT_CONFIG_DEFAULTS, loadUserLayer, mergeConfigLayers, MODULE_CONFIG_CONTRIBUTIONS, normalizeConfigForExport, PROJECT_CONFIG_REL, resolveWorkspaceConfigWithLayers, stableStringifyConfig } from "./workspace-kit-config.js";
|
|
4
4
|
export { appendPolicyTrace, getExtraSensitiveModuleCommandsFromEffective, getOperationIdForCommand, isSensitiveModuleCommand, isSensitiveModuleCommandForEffective, parsePolicyApproval, parsePolicyApprovalFromEnv, POLICY_TRACE_SCHEMA_VERSION, resolveActor, resolvePolicyOperationIdForCommand } from "./policy.js";
|
|
5
|
+
export { getSessionGrant, loadSessionPolicyDocument, recordSessionGrant, resolveSessionId, SESSION_POLICY_SCHEMA_VERSION } from "./session-policy.js";
|
|
6
|
+
export { parseTemplateDirectiveFromText } from "./instruction-template-mapper.js";
|
|
7
|
+
export { RESPONSE_TEMPLATE_CONTRACT_VERSION, MAX_TEMPLATE_WARNING_LENGTH, truncateTemplateWarning } from "./response-template-contract.js";
|
|
8
|
+
export { allBuiltinDefinitions, getResponseTemplateDefinition, listBuiltinResponseTemplateIds } from "./response-template-registry.js";
|
|
9
|
+
export { applyResponseTemplateApplication } from "./response-template-shaping.js";
|
|
10
|
+
export { maybeSpawnTranscriptHookAfterCompletion, readAfterTaskCompletedHook, resolveWorkspaceKitCli } from "./transcript-completion-hook.js";
|
|
5
11
|
export { assertWritableKey, getConfigKeyMetadata, getConfigRegistryExport, listConfigMetadata, validatePersistedConfigDocument, validateValueForMetadata } from "./config-metadata.js";
|
|
6
12
|
export { appendConfigMutation, CONFIG_MUTATIONS_SCHEMA_VERSION, summarizeForEvidence } from "./config-mutations.js";
|
|
7
13
|
export { generateConfigReferenceDocs, runWorkspaceConfigCli } from "./config-cli.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type TemplateDirectiveParseResult = {
|
|
2
|
+
templateId: string | null;
|
|
3
|
+
warnings: string[];
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a template id from free-form instruction text (T263).
|
|
7
|
+
* Examples: "Use the COMPLETED_TASK template", "template: compact"
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseTemplateDirectiveFromText(text: string): TemplateDirectiveParseResult;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { truncateTemplateWarning } from "./response-template-contract.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve a template id from free-form instruction text (T263).
|
|
4
|
+
* Examples: "Use the COMPLETED_TASK template", "template: compact"
|
|
5
|
+
*/
|
|
6
|
+
export function parseTemplateDirectiveFromText(text) {
|
|
7
|
+
const warnings = [];
|
|
8
|
+
if (!text || typeof text !== "string") {
|
|
9
|
+
return { templateId: null, warnings };
|
|
10
|
+
}
|
|
11
|
+
const s = text.trim();
|
|
12
|
+
if (!s)
|
|
13
|
+
return { templateId: null, warnings };
|
|
14
|
+
const ids = new Set();
|
|
15
|
+
const reUseThe = /\buse\s+the\s+([A-Za-z0-9_-]+)\s+template\b/gi;
|
|
16
|
+
let m;
|
|
17
|
+
while ((m = reUseThe.exec(s)) !== null) {
|
|
18
|
+
ids.add(m[1]);
|
|
19
|
+
}
|
|
20
|
+
const reTemplateEq = /\btemplate\s*[:=]\s*([A-Za-z0-9_-]+)\b/gi;
|
|
21
|
+
while ((m = reTemplateEq.exec(s)) !== null) {
|
|
22
|
+
ids.add(m[1]);
|
|
23
|
+
}
|
|
24
|
+
const reBare = /\b(?:responseTemplateId|templateId)\s+is\s+([A-Za-z0-9_-]+)\b/gi;
|
|
25
|
+
while ((m = reBare.exec(s)) !== null) {
|
|
26
|
+
ids.add(m[1]);
|
|
27
|
+
}
|
|
28
|
+
if (ids.size === 0) {
|
|
29
|
+
return { templateId: null, warnings };
|
|
30
|
+
}
|
|
31
|
+
if (ids.size > 1) {
|
|
32
|
+
warnings.push(truncateTemplateWarning("Ambiguous template directive: multiple template ids referenced; using first match."));
|
|
33
|
+
}
|
|
34
|
+
return { templateId: [...ids][0], warnings };
|
|
35
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* T203: Immutable lineage event contract (append-only store, correlation fields).
|
|
3
|
-
* @see
|
|
3
|
+
* @see .workspace-kit/tasks/state.json T192, T203
|
|
4
4
|
*/
|
|
5
5
|
export declare const LINEAGE_SCHEMA_VERSION: 1;
|
|
6
6
|
export type LineageEventType = "rec" | "dec" | "app" | "corr";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* T203: Immutable lineage event contract (append-only store, correlation fields).
|
|
3
|
-
* @see
|
|
3
|
+
* @see .workspace-kit/tasks/state.json T192, T203
|
|
4
4
|
*/
|
|
5
5
|
export const LINEAGE_SCHEMA_VERSION = 1;
|
|
6
6
|
/** Stable correlation root: ties chain to recommendation + evidence identity. */
|
package/dist/core/policy.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const POLICY_TRACE_SCHEMA_VERSION: 1;
|
|
2
|
-
export type PolicyOperationId = "cli.upgrade" | "cli.init" | "cli.config-mutate" | "policy.dynamic-sensitive" | "doc.document-project" | "doc.generate-document" | "tasks.
|
|
2
|
+
export type PolicyOperationId = "cli.upgrade" | "cli.init" | "cli.config-mutate" | "policy.dynamic-sensitive" | "doc.document-project" | "doc.generate-document" | "tasks.run-transition" | "approvals.review-item" | "improvement.generate-recommendations" | "improvement.ingest-transcripts";
|
|
3
3
|
export declare function getOperationIdForCommand(commandName: string): PolicyOperationId | undefined;
|
|
4
4
|
export declare function getExtraSensitiveModuleCommandsFromEffective(effective: Record<string, unknown>): string[];
|
|
5
5
|
/** Resolve operation id for tracing, including config-declared sensitive module commands. */
|
|
@@ -8,9 +8,12 @@ export declare function resolvePolicyOperationIdForCommand(commandName: string,
|
|
|
8
8
|
* Sensitive when mutation / write is possible. Documentation commands are exempt when dryRun is true.
|
|
9
9
|
*/
|
|
10
10
|
export declare function isSensitiveModuleCommand(commandName: string, args: Record<string, unknown>): boolean;
|
|
11
|
+
export type PolicyApprovalScope = "once" | "session";
|
|
11
12
|
export type PolicyApprovalPayload = {
|
|
12
13
|
confirmed: boolean;
|
|
13
14
|
rationale: string;
|
|
15
|
+
/** When `session`, persist approval for this operation until session id changes or grants file is cleared. */
|
|
16
|
+
scope?: PolicyApprovalScope;
|
|
14
17
|
};
|
|
15
18
|
export declare function parsePolicyApprovalFromEnv(env: NodeJS.ProcessEnv): PolicyApprovalPayload | undefined;
|
|
16
19
|
export declare function parsePolicyApproval(args: Record<string, unknown>): PolicyApprovalPayload | undefined;
|
package/dist/core/policy.js
CHANGED
|
@@ -5,11 +5,10 @@ export const POLICY_TRACE_SCHEMA_VERSION = 1;
|
|
|
5
5
|
const COMMAND_TO_OPERATION = {
|
|
6
6
|
"document-project": "doc.document-project",
|
|
7
7
|
"generate-document": "doc.generate-document",
|
|
8
|
-
"import-tasks": "tasks.import-tasks",
|
|
9
|
-
"generate-tasks-md": "tasks.generate-tasks-md",
|
|
10
8
|
"run-transition": "tasks.run-transition",
|
|
11
9
|
"review-item": "approvals.review-item",
|
|
12
|
-
"generate-recommendations": "improvement.generate-recommendations"
|
|
10
|
+
"generate-recommendations": "improvement.generate-recommendations",
|
|
11
|
+
"ingest-transcripts": "improvement.ingest-transcripts"
|
|
13
12
|
};
|
|
14
13
|
export function getOperationIdForCommand(commandName) {
|
|
15
14
|
return COMMAND_TO_OPERATION[commandName];
|
|
@@ -80,7 +79,9 @@ export function parsePolicyApproval(args) {
|
|
|
80
79
|
if (!confirmed || rationale.length === 0) {
|
|
81
80
|
return undefined;
|
|
82
81
|
}
|
|
83
|
-
|
|
82
|
+
const scopeRaw = o.scope;
|
|
83
|
+
const scope = scopeRaw === "session" || scopeRaw === "once" ? scopeRaw : undefined;
|
|
84
|
+
return { confirmed, rationale, ...(scope ? { scope } : {}) };
|
|
84
85
|
}
|
|
85
86
|
export function resolveActor(workspacePath, args, env) {
|
|
86
87
|
if (typeof args.actor === "string" && args.actor.trim().length > 0) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Contract version for response template definitions and CLI shaping metadata. */
|
|
2
|
+
export declare const RESPONSE_TEMPLATE_CONTRACT_VERSION: 1;
|
|
3
|
+
export type ResponseTemplateEnforcementMode = "advisory" | "strict";
|
|
4
|
+
export type ResponseTemplateDefinition = {
|
|
5
|
+
id: string;
|
|
6
|
+
/** Monotonic template definition revision for compatibility notes. */
|
|
7
|
+
version: number;
|
|
8
|
+
scope: "global" | "command";
|
|
9
|
+
description: string;
|
|
10
|
+
/** Logical section keys expected in shaped command `data` (advisory hints only). */
|
|
11
|
+
expectedSections: string[];
|
|
12
|
+
};
|
|
13
|
+
/** Max length for a single advisory warning line (T265 / T266). */
|
|
14
|
+
export declare const MAX_TEMPLATE_WARNING_LENGTH = 120;
|
|
15
|
+
export declare function truncateTemplateWarning(message: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Contract version for response template definitions and CLI shaping metadata. */
|
|
2
|
+
export const RESPONSE_TEMPLATE_CONTRACT_VERSION = 1;
|
|
3
|
+
/** Max length for a single advisory warning line (T265 / T266). */
|
|
4
|
+
export const MAX_TEMPLATE_WARNING_LENGTH = 120;
|
|
5
|
+
export function truncateTemplateWarning(message) {
|
|
6
|
+
const t = message.trim();
|
|
7
|
+
if (t.length <= MAX_TEMPLATE_WARNING_LENGTH)
|
|
8
|
+
return t;
|
|
9
|
+
return `${t.slice(0, MAX_TEMPLATE_WARNING_LENGTH - 1)}…`;
|
|
10
|
+
}
|