@tech-leads-club/harness-toolkit 0.5.1 → 0.7.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/bin/generate-schema.ts +67 -0
- package/bin/tlc-build.mjs +26 -1
- package/bin/tlc-cli.ts +43 -11
- package/config.example.json +1 -0
- package/dist/compact-before.mjs +83 -83
- package/dist/doctor.mjs +79 -79
- package/dist/init-project.mjs +87 -87
- package/dist/install-runtime.mjs +82 -82
- package/dist/lessons-cli.mjs +86 -86
- package/dist/obs-cli.mjs +79 -79
- package/dist/prompt-submit.mjs +83 -83
- package/dist/refresh-model-prices.mjs +82 -82
- package/dist/response-after.mjs +83 -83
- package/dist/run.mjs +83 -83
- package/dist/session-end.mjs +89 -89
- package/dist/session-start.mjs +91 -91
- package/dist/shim.mjs +81 -81
- package/dist/stop.mjs +89 -89
- package/dist/subagent-start.mjs +83 -83
- package/dist/subagent-stop.mjs +84 -84
- package/dist/support.mjs +87 -87
- package/dist/tlc-cli.mjs +103 -102
- package/dist/tool-after.mjs +83 -83
- package/dist/tool-before.mjs +85 -85
- package/dist/tool-failure.mjs +83 -83
- package/dist/uninstall-runtime.mjs +4 -4
- package/docs/concepts.md +4 -1
- package/docs/log.md +8 -0
- package/package.json +4 -2
- package/schema.json +545 -0
- package/src/core/capability/capability.store.ts +27 -1
- package/src/core/comment-policy/comment-policy.service.ts +3 -8
- package/src/core/core.facade.ts +7 -1
- package/src/core/floor/floor.policy-surface.ts +1 -1
- package/src/core/handoff/handoff.service.ts +30 -0
- package/src/core/handoff/handoff.types.ts +0 -2
- package/src/core/lesson/lesson.types.ts +5 -0
- package/src/core/policy/policy.loader.ts +5 -1
- package/src/core/policy/policy.shadow.ts +68 -2
- package/src/core/turn/turn.failure-signals.ts +3 -1
- package/src/entrypoints/stop.ts +16 -14
- package/src/entrypoints/subagent-stop.ts +8 -6
- package/tools/doctor.ts +48 -0
- package/tools/init-project.ts +19 -1
package/schema.json
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
{
|
|
2
|
+
"additionalProperties": false,
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"$schema": {
|
|
6
|
+
"type": "string"
|
|
7
|
+
},
|
|
8
|
+
"version": {
|
|
9
|
+
"const": 1,
|
|
10
|
+
"type": "number"
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"enum": [
|
|
14
|
+
"focus",
|
|
15
|
+
"paired",
|
|
16
|
+
"solo"
|
|
17
|
+
],
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"projectName": {
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
"codePaths": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"grind": {
|
|
30
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BmaxLoops%3Anumber%3BlintCommand%3Astring%5B%5D%7Cnull%3BtestCommand%3Astring%5B%5D%7Cnull%3BappendFiles%3Aimport(%22.%2Fsrc%2Fcore%2Fgate%2Fgate.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).AppendFilesMode%3B%7D%3E"
|
|
31
|
+
},
|
|
32
|
+
"shipGate": {
|
|
33
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BruntimePathPrefixes%3Astring%5B%5D%3BruntimePathExcludes%3Astring%5B%5D%3BevidenceDir%3Astring%7Cnull%3BevidenceMaxAgeHours%3Anumber%3BemptyDiffAntiShip%3Aboolean%3BclaimWindowMinutes%3Anumber%3B%7D%3E"
|
|
34
|
+
},
|
|
35
|
+
"subagents": {
|
|
36
|
+
"$ref": "#/definitions/Partial%3C%7BenforceAllowlist%3Aboolean%3BrequireModel%3Aboolean%3BallowedModels%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).ProviderScoped%3Cstring%3E%3BblockedPatterns%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).ProviderScoped%3Cstring%3E%3BminEffort%3A%22low%22%7C%22medium%22%7C%22high%22%7C%22xhigh%22%7C%22max%22%7Cnull%3BblockParentFast%3Aboolean%3BblockMode%3A%22deny%22%7C%22ask%22%3BreadOnlyTypes%3Astring%5B%5D%3B%7D%3E"
|
|
37
|
+
},
|
|
38
|
+
"docs": {
|
|
39
|
+
"$ref": "#/definitions/Partial%3C%7Bcommand%3Astring%5B%5D%7Cnull%3Bseverity%3A%22deny%22%7C%22warn%22%3B%7D%3E"
|
|
40
|
+
},
|
|
41
|
+
"observe": {
|
|
42
|
+
"description": "why: observation runs a rail's checker while the rail is not enforcing, so the harness can tell \"the model\nalready does this\" from \"the rule works\". Opt-in and default empty: it costs a diff scan per turn and answers a\nquestion only an operator who is asking it needs answered.",
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"enabled": {
|
|
46
|
+
"type": "boolean"
|
|
47
|
+
},
|
|
48
|
+
"rails": {
|
|
49
|
+
"description": "Rails to observe by name. An unknown name is inert rather than an error — a rail may not exist yet.",
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": [
|
|
58
|
+
"enabled",
|
|
59
|
+
"rails"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"comments": {
|
|
63
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BonViolation%3A%22followup%22%7C%22off%22%3Bmode%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).CommentMode%3B%7D%3E"
|
|
64
|
+
},
|
|
65
|
+
"supplyChain": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"properties": {
|
|
68
|
+
"enabled": {
|
|
69
|
+
"type": "boolean"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"required": [
|
|
74
|
+
"enabled"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"duplication": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"enabled": {
|
|
81
|
+
"type": "boolean"
|
|
82
|
+
},
|
|
83
|
+
"minRun": {
|
|
84
|
+
"description": "why: the window is the only knob, because it is the only one calibration moved. Six is where matches stopped being punctuation.",
|
|
85
|
+
"type": "number"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"required": [
|
|
90
|
+
"enabled",
|
|
91
|
+
"minRun"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"obs": {
|
|
95
|
+
"$ref": "#/definitions/Partial%3C%7BglobalSpool%3Aboolean%3BincludePayloads%3Aboolean%3BmaxAttrChars%3Anumber%3BsessionCostAlertUsd%3Anumber%7Cnull%3BretentionDays%3Anumber%3B%7D%3E"
|
|
96
|
+
},
|
|
97
|
+
"untrustedContent": {
|
|
98
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3Bmode%3Aimport(%22.%2Fsrc%2Fcore%2Funtrusted%2Funtrusted.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).UntrustedMode%3BextraTools%3Astring%5B%5D%3BextraCommandPatterns%3Astring%5B%5D%3B%7D%3E"
|
|
99
|
+
},
|
|
100
|
+
"planGate": {
|
|
101
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BwindowMinutes%3Anumber%3B%7D%3E"
|
|
102
|
+
},
|
|
103
|
+
"rules": {
|
|
104
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3B%7D%3E"
|
|
105
|
+
},
|
|
106
|
+
"shell": {
|
|
107
|
+
"$ref": "#/definitions/Partial%3C%7BcatastrophicAsk%3Aboolean%3BstallDetection%3Aboolean%3BstallRepeatThreshold%3Anumber%3B%7D%3E"
|
|
108
|
+
},
|
|
109
|
+
"intelligence": {
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"type": "object",
|
|
112
|
+
"properties": {
|
|
113
|
+
"gapFeedback": {
|
|
114
|
+
"type": "boolean"
|
|
115
|
+
},
|
|
116
|
+
"failureClassification": {
|
|
117
|
+
"type": "boolean"
|
|
118
|
+
},
|
|
119
|
+
"progressiveHandoff": {
|
|
120
|
+
"type": "boolean"
|
|
121
|
+
},
|
|
122
|
+
"progressiveContext": {
|
|
123
|
+
"type": "boolean"
|
|
124
|
+
},
|
|
125
|
+
"autopilot": {
|
|
126
|
+
"type": "boolean"
|
|
127
|
+
},
|
|
128
|
+
"idleTurnGate": {
|
|
129
|
+
"type": "boolean"
|
|
130
|
+
},
|
|
131
|
+
"budgetContinue": {
|
|
132
|
+
"type": "boolean"
|
|
133
|
+
},
|
|
134
|
+
"budgetContinueAfterLoops": {
|
|
135
|
+
"type": "number"
|
|
136
|
+
},
|
|
137
|
+
"lessons": {
|
|
138
|
+
"$ref": "#/definitions/Partial%3Cimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).LessonsPolicyConfig%3E"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"mcpPrime": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"items": {
|
|
145
|
+
"type": "string"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"bootstrapExtra": {
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": {
|
|
151
|
+
"type": "string"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"definitions": {
|
|
156
|
+
"AppendFilesMode": {
|
|
157
|
+
"enum": [
|
|
158
|
+
"always",
|
|
159
|
+
"auto",
|
|
160
|
+
"never"
|
|
161
|
+
],
|
|
162
|
+
"type": "string"
|
|
163
|
+
},
|
|
164
|
+
"import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>": {
|
|
165
|
+
"anyOf": [
|
|
166
|
+
{
|
|
167
|
+
"type": "array",
|
|
168
|
+
"items": {
|
|
169
|
+
"type": "string"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"Record<string,string[]>": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"additionalProperties": false
|
|
180
|
+
},
|
|
181
|
+
"CommentMode": {
|
|
182
|
+
"description": "`declared` requires a reason marker, `strict` accepts no added comment at all, and `resolvable` is `declared`\nplus the question neither can ask: does the comment mean anything to a reader who was not in the session that\nwrote it ([/decisions/ad-070.md](/decisions/ad-070.md))?",
|
|
183
|
+
"enum": [
|
|
184
|
+
"declared",
|
|
185
|
+
"resolvable",
|
|
186
|
+
"strict"
|
|
187
|
+
],
|
|
188
|
+
"type": "string"
|
|
189
|
+
},
|
|
190
|
+
"UntrustedMode": {
|
|
191
|
+
"description": "`frame` states once per turn that outside content is data. `enforce` adds the question framing cannot ask —\ndid this command come from that content — and answers it verbatim\n([/decisions/ad-077.md](/decisions/ad-077.md)).",
|
|
192
|
+
"enum": [
|
|
193
|
+
"enforce",
|
|
194
|
+
"frame"
|
|
195
|
+
],
|
|
196
|
+
"type": "string"
|
|
197
|
+
},
|
|
198
|
+
"LessonsSyncMode": {
|
|
199
|
+
"description": "How the durable provider view is decided. `auto` asks the host: a provider that does not deliver context from\nits session-start hook has no other route, so the view is written there and not where the hook works\n([/decisions/ad-050.md](/decisions/ad-050.md)).",
|
|
200
|
+
"enum": [
|
|
201
|
+
"always",
|
|
202
|
+
"auto",
|
|
203
|
+
"never"
|
|
204
|
+
],
|
|
205
|
+
"type": "string"
|
|
206
|
+
},
|
|
207
|
+
"Partial<{enabled:boolean;maxLoops:number;lintCommand:string[]|null;testCommand:string[]|null;appendFiles:import(\"./src/core/gate/gate.types\",{with:{\"resolution-mode\":\"import\"}}).AppendFilesMode;}>": {
|
|
208
|
+
"type": "object",
|
|
209
|
+
"properties": {
|
|
210
|
+
"enabled": {
|
|
211
|
+
"type": "boolean"
|
|
212
|
+
},
|
|
213
|
+
"maxLoops": {
|
|
214
|
+
"type": "number"
|
|
215
|
+
},
|
|
216
|
+
"lintCommand": {
|
|
217
|
+
"anyOf": [
|
|
218
|
+
{
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"type": "null"
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
"testCommand": {
|
|
230
|
+
"anyOf": [
|
|
231
|
+
{
|
|
232
|
+
"type": "array",
|
|
233
|
+
"items": {
|
|
234
|
+
"type": "string"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"type": "null"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
"appendFiles": {
|
|
243
|
+
"enum": [
|
|
244
|
+
"always",
|
|
245
|
+
"auto",
|
|
246
|
+
"never"
|
|
247
|
+
],
|
|
248
|
+
"type": "string"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"additionalProperties": false
|
|
252
|
+
},
|
|
253
|
+
"Partial<{enabled:boolean;runtimePathPrefixes:string[];runtimePathExcludes:string[];evidenceDir:string|null;evidenceMaxAgeHours:number;emptyDiffAntiShip:boolean;claimWindowMinutes:number;}>": {
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"enabled": {
|
|
257
|
+
"type": "boolean"
|
|
258
|
+
},
|
|
259
|
+
"runtimePathPrefixes": {
|
|
260
|
+
"type": "array",
|
|
261
|
+
"items": {
|
|
262
|
+
"type": "string"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"runtimePathExcludes": {
|
|
266
|
+
"type": "array",
|
|
267
|
+
"items": {
|
|
268
|
+
"type": "string"
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"evidenceDir": {
|
|
272
|
+
"type": [
|
|
273
|
+
"null",
|
|
274
|
+
"string"
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
"evidenceMaxAgeHours": {
|
|
278
|
+
"type": "number"
|
|
279
|
+
},
|
|
280
|
+
"emptyDiffAntiShip": {
|
|
281
|
+
"type": "boolean"
|
|
282
|
+
},
|
|
283
|
+
"claimWindowMinutes": {
|
|
284
|
+
"type": "number"
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"additionalProperties": false
|
|
288
|
+
},
|
|
289
|
+
"Partial<{enforceAllowlist:boolean;requireModel:boolean;allowedModels:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>;blockedPatterns:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>;minEffort:\"low\"|\"medium\"|\"high\"|\"xhigh\"|\"max\"|null;blockParentFast:boolean;blockMode:\"deny\"|\"ask\";readOnlyTypes:string[];}>": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"properties": {
|
|
292
|
+
"enforceAllowlist": {
|
|
293
|
+
"type": "boolean"
|
|
294
|
+
},
|
|
295
|
+
"requireModel": {
|
|
296
|
+
"type": "boolean"
|
|
297
|
+
},
|
|
298
|
+
"allowedModels": {
|
|
299
|
+
"anyOf": [
|
|
300
|
+
{
|
|
301
|
+
"type": "array",
|
|
302
|
+
"items": {
|
|
303
|
+
"type": "string"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
},
|
|
311
|
+
"blockedPatterns": {
|
|
312
|
+
"anyOf": [
|
|
313
|
+
{
|
|
314
|
+
"type": "array",
|
|
315
|
+
"items": {
|
|
316
|
+
"type": "string"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
"minEffort": {
|
|
325
|
+
"anyOf": [
|
|
326
|
+
{
|
|
327
|
+
"enum": [
|
|
328
|
+
"high",
|
|
329
|
+
"low",
|
|
330
|
+
"max",
|
|
331
|
+
"medium",
|
|
332
|
+
"xhigh"
|
|
333
|
+
],
|
|
334
|
+
"type": "string"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"type": "null"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"blockParentFast": {
|
|
342
|
+
"type": "boolean"
|
|
343
|
+
},
|
|
344
|
+
"blockMode": {
|
|
345
|
+
"enum": [
|
|
346
|
+
"ask",
|
|
347
|
+
"deny"
|
|
348
|
+
],
|
|
349
|
+
"type": "string"
|
|
350
|
+
},
|
|
351
|
+
"readOnlyTypes": {
|
|
352
|
+
"type": "array",
|
|
353
|
+
"items": {
|
|
354
|
+
"type": "string"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
"additionalProperties": false
|
|
359
|
+
},
|
|
360
|
+
"Partial<{command:string[]|null;severity:\"deny\"|\"warn\";}>": {
|
|
361
|
+
"type": "object",
|
|
362
|
+
"properties": {
|
|
363
|
+
"command": {
|
|
364
|
+
"description": "Null means the gate does not exist for this project.",
|
|
365
|
+
"anyOf": [
|
|
366
|
+
{
|
|
367
|
+
"type": "array",
|
|
368
|
+
"items": {
|
|
369
|
+
"type": "string"
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"type": "null"
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
},
|
|
377
|
+
"severity": {
|
|
378
|
+
"enum": [
|
|
379
|
+
"deny",
|
|
380
|
+
"warn"
|
|
381
|
+
],
|
|
382
|
+
"type": "string"
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"additionalProperties": false
|
|
386
|
+
},
|
|
387
|
+
"Partial<{enabled:boolean;onViolation:\"followup\"|\"off\";mode:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).CommentMode;}>": {
|
|
388
|
+
"type": "object",
|
|
389
|
+
"properties": {
|
|
390
|
+
"enabled": {
|
|
391
|
+
"type": "boolean"
|
|
392
|
+
},
|
|
393
|
+
"onViolation": {
|
|
394
|
+
"enum": [
|
|
395
|
+
"followup",
|
|
396
|
+
"off"
|
|
397
|
+
],
|
|
398
|
+
"type": "string"
|
|
399
|
+
},
|
|
400
|
+
"mode": {
|
|
401
|
+
"description": "`declared` requires a reason marker, `strict` accepts no added comment at all, and `resolvable` is `declared`\nplus the question neither can ask: does the comment mean anything to a reader who was not in the session that\nwrote it ([/decisions/ad-070.md](/decisions/ad-070.md))?",
|
|
402
|
+
"enum": [
|
|
403
|
+
"declared",
|
|
404
|
+
"resolvable",
|
|
405
|
+
"strict"
|
|
406
|
+
],
|
|
407
|
+
"type": "string"
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
"additionalProperties": false
|
|
411
|
+
},
|
|
412
|
+
"Partial<{globalSpool:boolean;includePayloads:boolean;maxAttrChars:number;sessionCostAlertUsd:number|null;retentionDays:number;}>": {
|
|
413
|
+
"type": "object",
|
|
414
|
+
"properties": {
|
|
415
|
+
"globalSpool": {
|
|
416
|
+
"type": "boolean"
|
|
417
|
+
},
|
|
418
|
+
"includePayloads": {
|
|
419
|
+
"type": "boolean"
|
|
420
|
+
},
|
|
421
|
+
"maxAttrChars": {
|
|
422
|
+
"type": "number"
|
|
423
|
+
},
|
|
424
|
+
"sessionCostAlertUsd": {
|
|
425
|
+
"type": [
|
|
426
|
+
"null",
|
|
427
|
+
"number"
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
"retentionDays": {
|
|
431
|
+
"type": "number"
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
"additionalProperties": false
|
|
435
|
+
},
|
|
436
|
+
"Partial<{enabled:boolean;mode:import(\"./src/core/untrusted/untrusted.types\",{with:{\"resolution-mode\":\"import\"}}).UntrustedMode;extraTools:string[];extraCommandPatterns:string[];}>": {
|
|
437
|
+
"type": "object",
|
|
438
|
+
"properties": {
|
|
439
|
+
"enabled": {
|
|
440
|
+
"type": "boolean"
|
|
441
|
+
},
|
|
442
|
+
"mode": {
|
|
443
|
+
"description": "`frame` states once per turn that outside content is data. `enforce` adds the question framing cannot ask —\ndid this command come from that content ([/decisions/ad-077.md](/decisions/ad-077.md))?",
|
|
444
|
+
"enum": [
|
|
445
|
+
"enforce",
|
|
446
|
+
"frame"
|
|
447
|
+
],
|
|
448
|
+
"type": "string"
|
|
449
|
+
},
|
|
450
|
+
"extraTools": {
|
|
451
|
+
"type": "array",
|
|
452
|
+
"items": {
|
|
453
|
+
"type": "string"
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
"extraCommandPatterns": {
|
|
457
|
+
"type": "array",
|
|
458
|
+
"items": {
|
|
459
|
+
"type": "string"
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
"additionalProperties": false
|
|
464
|
+
},
|
|
465
|
+
"Partial<{enabled:boolean;windowMinutes:number;}>": {
|
|
466
|
+
"type": "object",
|
|
467
|
+
"properties": {
|
|
468
|
+
"enabled": {
|
|
469
|
+
"type": "boolean"
|
|
470
|
+
},
|
|
471
|
+
"windowMinutes": {
|
|
472
|
+
"type": "number"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
"additionalProperties": false
|
|
476
|
+
},
|
|
477
|
+
"Partial<{enabled:boolean;}>": {
|
|
478
|
+
"type": "object",
|
|
479
|
+
"properties": {
|
|
480
|
+
"enabled": {
|
|
481
|
+
"type": "boolean"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"additionalProperties": false
|
|
485
|
+
},
|
|
486
|
+
"Partial<{catastrophicAsk:boolean;stallDetection:boolean;stallRepeatThreshold:number;}>": {
|
|
487
|
+
"type": "object",
|
|
488
|
+
"properties": {
|
|
489
|
+
"catastrophicAsk": {
|
|
490
|
+
"type": "boolean"
|
|
491
|
+
},
|
|
492
|
+
"stallDetection": {
|
|
493
|
+
"type": "boolean"
|
|
494
|
+
},
|
|
495
|
+
"stallRepeatThreshold": {
|
|
496
|
+
"type": "number"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
"additionalProperties": false
|
|
500
|
+
},
|
|
501
|
+
"Partial<import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).LessonsPolicyConfig>": {
|
|
502
|
+
"type": "object",
|
|
503
|
+
"properties": {
|
|
504
|
+
"enabled": {
|
|
505
|
+
"type": "boolean"
|
|
506
|
+
},
|
|
507
|
+
"maxInjectSession": {
|
|
508
|
+
"type": "number"
|
|
509
|
+
},
|
|
510
|
+
"maxInjectRetry": {
|
|
511
|
+
"type": "number"
|
|
512
|
+
},
|
|
513
|
+
"maxCharsSession": {
|
|
514
|
+
"type": "number"
|
|
515
|
+
},
|
|
516
|
+
"maxCharsRetry": {
|
|
517
|
+
"type": "number"
|
|
518
|
+
},
|
|
519
|
+
"promoteHitCount": {
|
|
520
|
+
"type": "number"
|
|
521
|
+
},
|
|
522
|
+
"decayLambda": {
|
|
523
|
+
"type": "number"
|
|
524
|
+
},
|
|
525
|
+
"projectBoost": {
|
|
526
|
+
"type": "number"
|
|
527
|
+
},
|
|
528
|
+
"syncRulesFile": {
|
|
529
|
+
"description": "How the durable provider view is decided. `auto` asks the host: a provider that does not deliver context from\nits session-start hook has no other route, so the view is written there and not where the hook works\n([/decisions/ad-050.md](/decisions/ad-050.md)).",
|
|
530
|
+
"enum": [
|
|
531
|
+
"always",
|
|
532
|
+
"auto",
|
|
533
|
+
"never"
|
|
534
|
+
],
|
|
535
|
+
"type": "string"
|
|
536
|
+
},
|
|
537
|
+
"gardenOnSessionEnd": {
|
|
538
|
+
"type": "boolean"
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
"additionalProperties": false
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
545
|
+
}
|
|
@@ -27,8 +27,34 @@ export function loadCatalog(home = runtimeHome()): CapabilityCatalog | null {
|
|
|
27
27
|
return raw;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export type ConfigReadStatus =
|
|
31
|
+
| { status: "absent" }
|
|
32
|
+
| { status: "parsed"; value: Record<string, unknown> }
|
|
33
|
+
| { status: "malformed"; error: string };
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* why: `readJson` already reads this file but collapses "absent" and "malformed" to the same `null`
|
|
37
|
+
* — the exact silent-corruption gap `doctor` needs to close, so this is a second, tagged reader.
|
|
38
|
+
*/
|
|
39
|
+
export function readProjectPolicyStatus(projectDir: string): ConfigReadStatus {
|
|
40
|
+
const path = projectConfigPath(projectDir);
|
|
41
|
+
if (!existsSync(path)) {
|
|
42
|
+
return { status: "absent" };
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
|
46
|
+
// why: a JSON Schema meta-key, not a Policy field — see the identical strip in policy.loader.ts,
|
|
47
|
+
// this is the second, independent read of the same file.
|
|
48
|
+
delete parsed.$schema;
|
|
49
|
+
return { status: "parsed", value: parsed };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return { status: "malformed", error: error instanceof Error ? error.message : String(error) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
30
55
|
export function readProjectPolicyRaw(projectDir: string): Record<string, unknown> | null {
|
|
31
|
-
|
|
56
|
+
const result = readProjectPolicyStatus(projectDir);
|
|
57
|
+
return result.status === "parsed" ? result.value : null;
|
|
32
58
|
}
|
|
33
59
|
|
|
34
60
|
function runtimeSeenPath(projectDir: string): string {
|
|
@@ -62,14 +62,9 @@ export function isCommentLine(text: string, file = ""): boolean {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* config and zero comments ever scanned. This filters by the same source the scanner itself trusts.
|
|
69
|
-
*
|
|
70
|
-
* invariant: takes no `codePaths` — that would need `policy.loader.ts`, which imports `policy.defaults.ts`,
|
|
71
|
-
* which imports `duplication.service.ts`, which imports this module for `matchesSyntax`. A caller scopes by
|
|
72
|
-
* `codePaths` itself first; this filters what is left by syntax alone.
|
|
65
|
+
* invariant: no `codePaths` param. Taking one needs `policy.loader.ts` → `policy.defaults.ts` →
|
|
66
|
+
* `duplication.service.ts`, which imports this module back for `matchesSyntax` — an import cycle. The
|
|
67
|
+
* caller scopes by `codePaths` first; this filters what's left by syntax alone.
|
|
73
68
|
*/
|
|
74
69
|
export function filterCommentTargets(relativePaths: string[]): string[] {
|
|
75
70
|
return relativePaths.filter((path) => syntaxFor(path) !== null);
|
package/src/core/core.facade.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
loadCatalog,
|
|
18
18
|
readProjectPolicyRaw,
|
|
19
|
+
readProjectPolicyStatus,
|
|
19
20
|
readRuntimeSeen,
|
|
20
21
|
writeRuntimeSeen,
|
|
21
22
|
} from "./capability/capability.store.ts";
|
|
@@ -48,6 +49,7 @@ import { cachedVerdict, computeInputsHash, isCacheHit } from "./gate/gate.inputs
|
|
|
48
49
|
import { describeHolder, GATE_LOCK_WAIT_MS, GateLockTimeoutError, withGateLock } from "./gate/gate.lock.ts";
|
|
49
50
|
import { gapsFromArtifact } from "./gate/gate.service.ts";
|
|
50
51
|
import {
|
|
52
|
+
clearStuckSignals,
|
|
51
53
|
handoffInjectable,
|
|
52
54
|
patchHandoff,
|
|
53
55
|
readForeignSlices,
|
|
@@ -133,7 +135,7 @@ import {
|
|
|
133
135
|
import { operatorBootstrapLines } from "./policy/policy.operator.ts";
|
|
134
136
|
import { isOperatorMode, OPERATOR_MODES } from "./policy/policy.posture.ts";
|
|
135
137
|
import { activeRails } from "./policy/policy.rails.ts";
|
|
136
|
-
import { pruneShadowed, shadowedKeys } from "./policy/policy.shadow.ts";
|
|
138
|
+
import { pruneShadowed, shadowedKeys, typeMismatches, unknownKeys } from "./policy/policy.shadow.ts";
|
|
137
139
|
import { forProvider } from "./policy/policy.types.ts";
|
|
138
140
|
import { checkCollision, heartbeat, register, release, sweepStale } from "./presence/presence.service.ts";
|
|
139
141
|
import { freshness, freshnessMessage, mayReplace, shouldRefetch } from "./pricing/pricing.freshness.ts";
|
|
@@ -264,6 +266,7 @@ export const coreFacade = {
|
|
|
264
266
|
ENABLE_HINT,
|
|
265
267
|
loadCatalog,
|
|
266
268
|
readProjectPolicyRaw,
|
|
269
|
+
readProjectPolicyStatus,
|
|
267
270
|
readRuntimeSeen,
|
|
268
271
|
writeRuntimeSeen,
|
|
269
272
|
isAvailableNotEnabled,
|
|
@@ -306,6 +309,7 @@ export const coreFacade = {
|
|
|
306
309
|
readHandoff,
|
|
307
310
|
readHandoffFile,
|
|
308
311
|
readForeignSlices,
|
|
312
|
+
clearStuckSignals,
|
|
309
313
|
},
|
|
310
314
|
shim: {
|
|
311
315
|
coversHandler,
|
|
@@ -416,6 +420,8 @@ export const coreFacade = {
|
|
|
416
420
|
resolvedWithoutProjectTier,
|
|
417
421
|
shadowedKeys,
|
|
418
422
|
pruneShadowed,
|
|
423
|
+
unknownKeys,
|
|
424
|
+
typeMismatches,
|
|
419
425
|
resolveProjectPosture,
|
|
420
426
|
resolveProjectSyncMode,
|
|
421
427
|
OPERATOR_MODES,
|
|
@@ -99,7 +99,7 @@ const HARNESS_BINS = new Set(["tlc", "tlc.cmd"]);
|
|
|
99
99
|
// why: `policy` is here for the sharpest reason of any of them — `tlc harness policy accept` exists to clear a
|
|
100
100
|
// tampering signal, so an agent that could reach it would make the whole integrity rail decorative. It is refused
|
|
101
101
|
// by a floor rule with no config switch, exactly like the rest ([/decisions/ad-030.md](/decisions/ad-030.md)).
|
|
102
|
-
const MUTATING_SUBCOMMANDS = new Set(["pause", "resume", "grind", "mode", "init", "gate", "policy"]);
|
|
102
|
+
const MUTATING_SUBCOMMANDS = new Set(["pause", "resume", "reset", "grind", "mode", "init", "gate", "policy"]);
|
|
103
103
|
|
|
104
104
|
function deny(detail: string, note: string, remedy?: string): PolicySurfaceVerdict {
|
|
105
105
|
return { kind: "deny", detail, note, ...(remedy ? { remedy } : {}) };
|