@workflow-cannon/workspace-kit 0.7.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.
Files changed (44) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.js +31 -21
  3. package/dist/contracts/index.d.ts +1 -1
  4. package/dist/contracts/module-contract.d.ts +13 -0
  5. package/dist/core/config-metadata.js +197 -3
  6. package/dist/core/index.d.ts +6 -0
  7. package/dist/core/index.js +6 -0
  8. package/dist/core/instruction-template-mapper.d.ts +9 -0
  9. package/dist/core/instruction-template-mapper.js +35 -0
  10. package/dist/core/lineage-contract.d.ts +1 -1
  11. package/dist/core/lineage-contract.js +1 -1
  12. package/dist/core/policy.d.ts +4 -1
  13. package/dist/core/policy.js +3 -3
  14. package/dist/core/response-template-contract.d.ts +15 -0
  15. package/dist/core/response-template-contract.js +10 -0
  16. package/dist/core/response-template-registry.d.ts +4 -0
  17. package/dist/core/response-template-registry.js +44 -0
  18. package/dist/core/response-template-shaping.d.ts +6 -0
  19. package/dist/core/response-template-shaping.js +128 -0
  20. package/dist/core/session-policy.d.ts +18 -0
  21. package/dist/core/session-policy.js +57 -0
  22. package/dist/core/transcript-completion-hook.d.ts +7 -0
  23. package/dist/core/transcript-completion-hook.js +90 -0
  24. package/dist/core/workspace-kit-config.js +25 -4
  25. package/dist/modules/documentation/runtime.js +383 -14
  26. package/dist/modules/improvement/generate-recommendations-runtime.d.ts +7 -0
  27. package/dist/modules/improvement/generate-recommendations-runtime.js +37 -4
  28. package/dist/modules/improvement/improvement-state.d.ts +10 -1
  29. package/dist/modules/improvement/improvement-state.js +36 -7
  30. package/dist/modules/improvement/index.js +55 -20
  31. package/dist/modules/improvement/ingest.js +2 -1
  32. package/dist/modules/improvement/transcript-redaction.d.ts +4 -0
  33. package/dist/modules/improvement/transcript-redaction.js +10 -0
  34. package/dist/modules/improvement/transcript-sync-runtime.d.ts +37 -1
  35. package/dist/modules/improvement/transcript-sync-runtime.js +198 -9
  36. package/dist/modules/index.d.ts +1 -1
  37. package/dist/modules/index.js +1 -1
  38. package/dist/modules/task-engine/index.d.ts +0 -2
  39. package/dist/modules/task-engine/index.js +4 -78
  40. package/package.json +5 -2
  41. package/dist/modules/task-engine/generator.d.ts +0 -3
  42. package/dist/modules/task-engine/generator.js +0 -118
  43. package/dist/modules/task-engine/importer.d.ts +0 -8
  44. package/dist/modules/task-engine/importer.js +0 -163
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 `docs/maintainers/TASKS.md` and `docs/maintainers/ROADMAP.md`.
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
- - `docs/maintainers/TASKS.md` - execution tracking
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: `docs/maintainers/TASKS.md`
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
- const approval = parsePolicyApproval(commandArgs);
644
- if (!approval) {
645
- const op = resolvePolicyOperationIdForCommand(subcommand, effective);
646
- if (op) {
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: op,
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":"user approved in chat"}}'
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 result = await router.execute(subcommand, commandArgs, ctx);
673
- if (sensitive) {
674
- const approval = parsePolicyApproval(commandArgs);
675
- const op = resolvePolicyOperationIdForCommand(subcommand, effective);
676
- if (approval && op) {
677
- await appendPolicyTrace(cwd, {
678
- timestamp: new Date().toISOString(),
679
- operationId: op,
680
- command: `run ${subcommand}`,
681
- actor,
682
- allowed: true,
683
- rationale: approval.rationale,
684
- commandOk: result.ok,
685
- message: result.message
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 = {
@@ -80,6 +80,125 @@ const REGISTRY = {
80
80
  requiresApproval: false,
81
81
  exposure: "maintainer",
82
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"]
83
202
  }
84
203
  };
85
204
  export function getConfigKeyMetadata(key) {
@@ -120,6 +239,13 @@ export function validateValueForMetadata(meta, value) {
120
239
  }
121
240
  }
122
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
+ }
123
249
  return;
124
250
  }
125
251
  if (meta.type === "string" && typeof value !== "string") {
@@ -135,6 +261,16 @@ export function validateValueForMetadata(meta, value) {
135
261
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
136
262
  throw typeError(meta.key, "object", value);
137
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
+ }
138
274
  }
139
275
  if (meta.allowedValues && meta.allowedValues.length > 0) {
140
276
  if (!meta.allowedValues.some((v) => deepEqualLoose(v, value))) {
@@ -161,6 +297,7 @@ export function validatePersistedConfigDocument(data, label) {
161
297
  "documentation",
162
298
  "policy",
163
299
  "improvement",
300
+ "responseTemplates",
164
301
  "modules"
165
302
  ]);
166
303
  for (const k of Object.keys(data)) {
@@ -223,7 +360,7 @@ export function validatePersistedConfigDocument(data, label) {
223
360
  }
224
361
  const imp = improvement;
225
362
  for (const k of Object.keys(imp)) {
226
- if (k !== "transcripts" && k !== "cadence") {
363
+ if (k !== "transcripts" && k !== "cadence" && k !== "hooks") {
227
364
  throw new Error(`config-invalid(${label}): unknown improvement.${k}`);
228
365
  }
229
366
  }
@@ -235,7 +372,12 @@ export function validatePersistedConfigDocument(data, label) {
235
372
  }
236
373
  const tr = imp.transcripts;
237
374
  for (const k of Object.keys(tr)) {
238
- if (k !== "sourcePath" && k !== "archivePath") {
375
+ if (k !== "sourcePath" &&
376
+ k !== "archivePath" &&
377
+ k !== "maxFilesPerSync" &&
378
+ k !== "maxBytesPerFile" &&
379
+ k !== "maxTotalScanBytes" &&
380
+ k !== "discoveryPaths") {
239
381
  throw new Error(`config-invalid(${label}): unknown improvement.transcripts.${k}`);
240
382
  }
241
383
  }
@@ -245,6 +387,18 @@ export function validatePersistedConfigDocument(data, label) {
245
387
  if (tr.archivePath !== undefined) {
246
388
  validateValueForMetadata(REGISTRY["improvement.transcripts.archivePath"], tr.archivePath);
247
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
+ }
248
402
  }
249
403
  if (imp.cadence !== undefined) {
250
404
  if (typeof imp.cadence !== "object" || imp.cadence === null || Array.isArray(imp.cadence)) {
@@ -252,7 +406,7 @@ export function validatePersistedConfigDocument(data, label) {
252
406
  }
253
407
  const cd = imp.cadence;
254
408
  for (const k of Object.keys(cd)) {
255
- if (k !== "minIntervalMinutes" && k !== "skipIfNoNewTranscripts") {
409
+ if (k !== "minIntervalMinutes" && k !== "skipIfNoNewTranscripts" && k !== "maxRecommendationCandidatesPerRun") {
256
410
  throw new Error(`config-invalid(${label}): unknown improvement.cadence.${k}`);
257
411
  }
258
412
  }
@@ -262,6 +416,46 @@ export function validatePersistedConfigDocument(data, label) {
262
416
  if (cd.skipIfNoNewTranscripts !== undefined) {
263
417
  validateValueForMetadata(REGISTRY["improvement.cadence.skipIfNoNewTranscripts"], cd.skipIfNoNewTranscripts);
264
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);
265
459
  }
266
460
  }
267
461
  }
@@ -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";
@@ -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 docs/maintainers/TASKS.md T192, T203
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 docs/maintainers/TASKS.md T192, T203
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. */
@@ -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.import-tasks" | "tasks.generate-tasks-md" | "tasks.run-transition" | "approvals.review-item" | "improvement.generate-recommendations" | "improvement.ingest-transcripts";
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;
@@ -5,8 +5,6 @@ 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
10
  "generate-recommendations": "improvement.generate-recommendations",
@@ -81,7 +79,9 @@ export function parsePolicyApproval(args) {
81
79
  if (!confirmed || rationale.length === 0) {
82
80
  return undefined;
83
81
  }
84
- return { confirmed, rationale };
82
+ const scopeRaw = o.scope;
83
+ const scope = scopeRaw === "session" || scopeRaw === "once" ? scopeRaw : undefined;
84
+ return { confirmed, rationale, ...(scope ? { scope } : {}) };
85
85
  }
86
86
  export function resolveActor(workspacePath, args, env) {
87
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
+ }
@@ -0,0 +1,4 @@
1
+ import type { ResponseTemplateDefinition } from "./response-template-contract.js";
2
+ export declare function getResponseTemplateDefinition(id: string | undefined): ResponseTemplateDefinition | undefined;
3
+ export declare function listBuiltinResponseTemplateIds(): string[];
4
+ export declare function allBuiltinDefinitions(): ResponseTemplateDefinition[];
@@ -0,0 +1,44 @@
1
+ const BUILTIN = {
2
+ default: {
3
+ id: "default",
4
+ version: 1,
5
+ scope: "global",
6
+ description: "Passthrough with standard ok/code/message/data fields.",
7
+ expectedSections: ["ok", "code", "message", "data"]
8
+ },
9
+ compact: {
10
+ id: "compact",
11
+ version: 1,
12
+ scope: "global",
13
+ description: "Emphasizes top-level code and message for quick scanning.",
14
+ expectedSections: ["ok", "code", "message"]
15
+ },
16
+ completed_task: {
17
+ id: "completed_task",
18
+ version: 1,
19
+ scope: "command",
20
+ description: "Structured completion-style surface for task-style commands.",
21
+ expectedSections: ["ok", "code", "message", "data"]
22
+ },
23
+ COMPLETED_TASK: {
24
+ id: "COMPLETED_TASK",
25
+ version: 1,
26
+ scope: "command",
27
+ description: "Alias id matching plain-English directive spelling.",
28
+ expectedSections: ["ok", "code", "message", "data"]
29
+ }
30
+ };
31
+ export function getResponseTemplateDefinition(id) {
32
+ if (!id || typeof id !== "string")
33
+ return undefined;
34
+ const trimmed = id.trim();
35
+ if (!trimmed)
36
+ return undefined;
37
+ return BUILTIN[trimmed] ?? BUILTIN[trimmed.toLowerCase()] ?? undefined;
38
+ }
39
+ export function listBuiltinResponseTemplateIds() {
40
+ return Object.keys(BUILTIN).sort((a, b) => a.localeCompare(b));
41
+ }
42
+ export function allBuiltinDefinitions() {
43
+ return Object.values(BUILTIN);
44
+ }
@@ -0,0 +1,6 @@
1
+ import type { ModuleCommandResult } from "../contracts/module-contract.js";
2
+ /**
3
+ * Apply response template metadata and optional presentation hints (T262, T265).
4
+ * Advisory mode never flips `ok`. Strict mode fails closed on unknown template ids when a template was explicitly requested or command override is set.
5
+ */
6
+ export declare function applyResponseTemplateApplication(commandName: string, args: Record<string, unknown>, result: ModuleCommandResult, effective: Record<string, unknown>): ModuleCommandResult;