@lannguyensi/harness 0.56.0 → 0.58.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 (126) hide show
  1. package/CHANGELOG.md +186 -0
  2. package/README.md +1 -1
  3. package/dist/cli/apply/apply.js +4 -3
  4. package/dist/cli/apply/apply.js.map +1 -1
  5. package/dist/cli/apply/generate-codex-config.js +3 -3
  6. package/dist/cli/apply/generate-codex-config.js.map +1 -1
  7. package/dist/cli/apply/generate-memory-index.js +20 -10
  8. package/dist/cli/apply/generate-memory-index.js.map +1 -1
  9. package/dist/cli/apply/generate-settings.d.ts +7 -0
  10. package/dist/cli/apply/generate-settings.js +10 -1
  11. package/dist/cli/apply/generate-settings.js.map +1 -1
  12. package/dist/cli/approve/understanding.d.ts +4 -4
  13. package/dist/cli/doctor/format.js +68 -2
  14. package/dist/cli/doctor/format.js.map +1 -1
  15. package/dist/cli/doctor/index.d.ts +6 -0
  16. package/dist/cli/doctor/index.js +214 -28
  17. package/dist/cli/doctor/index.js.map +1 -1
  18. package/dist/cli/doctor/session-start-preflight-setup-version.d.ts +83 -0
  19. package/dist/cli/doctor/session-start-preflight-setup-version.js +114 -0
  20. package/dist/cli/doctor/session-start-preflight-setup-version.js.map +1 -0
  21. package/dist/cli/doctor/types.d.ts +35 -4
  22. package/dist/cli/doctor/types.js +2 -1
  23. package/dist/cli/doctor/types.js.map +1 -1
  24. package/dist/cli/doctor/ug-delegations.js +9 -0
  25. package/dist/cli/doctor/ug-delegations.js.map +1 -1
  26. package/dist/cli/explain-policy.d.ts +67 -0
  27. package/dist/cli/explain-policy.js +140 -3
  28. package/dist/cli/explain-policy.js.map +1 -1
  29. package/dist/cli/gc/index.js +28 -14
  30. package/dist/cli/gc/index.js.map +1 -1
  31. package/dist/cli/index.d.ts +1 -0
  32. package/dist/cli/index.js +10 -7
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/init/composer.js +4 -3
  35. package/dist/cli/init/composer.js.map +1 -1
  36. package/dist/cli/init/dependencies.js +15 -9
  37. package/dist/cli/init/dependencies.js.map +1 -1
  38. package/dist/cli/init/templates.d.ts +1 -0
  39. package/dist/cli/init/templates.js +30 -4
  40. package/dist/cli/init/templates.js.map +1 -1
  41. package/dist/cli/list.js +19 -0
  42. package/dist/cli/list.js.map +1 -1
  43. package/dist/cli/loader.js +16 -1
  44. package/dist/cli/loader.js.map +1 -1
  45. package/dist/cli/pack/hook-bootstrap.js +2 -1
  46. package/dist/cli/pack/hook-bootstrap.js.map +1 -1
  47. package/dist/cli/pack/hook-codex-user-prompt-submit.d.ts +0 -32
  48. package/dist/cli/pack/hook-codex-user-prompt-submit.js +11 -118
  49. package/dist/cli/pack/hook-codex-user-prompt-submit.js.map +1 -1
  50. package/dist/cli/pack/hook-pre-tool-use.js +34 -7
  51. package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
  52. package/dist/cli/pack/hook-solution-acceptance.d.ts +55 -0
  53. package/dist/cli/pack/hook-solution-acceptance.js +222 -16
  54. package/dist/cli/pack/hook-solution-acceptance.js.map +1 -1
  55. package/dist/cli/pack/hook-stay-in-scope.d.ts +5 -26
  56. package/dist/cli/pack/hook-stay-in-scope.js +62 -274
  57. package/dist/cli/pack/hook-stay-in-scope.js.map +1 -1
  58. package/dist/cli/pack/hook-track-active-claim.d.ts +1 -0
  59. package/dist/cli/pack/hook-track-active-claim.js +107 -43
  60. package/dist/cli/pack/hook-track-active-claim.js.map +1 -1
  61. package/dist/cli/session-start/index.d.ts +32 -1
  62. package/dist/cli/session-start/index.js +142 -12
  63. package/dist/cli/session-start/index.js.map +1 -1
  64. package/dist/cli/validate/checks.d.ts +0 -2
  65. package/dist/cli/validate/checks.js +30 -30
  66. package/dist/cli/validate/checks.js.map +1 -1
  67. package/dist/io/harness-lock.js +11 -1
  68. package/dist/io/harness-lock.js.map +1 -1
  69. package/dist/io/lock.d.ts +26 -0
  70. package/dist/io/lock.js +25 -0
  71. package/dist/io/lock.js.map +1 -1
  72. package/dist/io/project-name.d.ts +115 -0
  73. package/dist/io/project-name.js +158 -0
  74. package/dist/io/project-name.js.map +1 -0
  75. package/dist/io/read-json-dir-entries.js +10 -0
  76. package/dist/io/read-json-dir-entries.js.map +1 -1
  77. package/dist/io/version-compare.d.ts +59 -0
  78. package/dist/io/version-compare.js +67 -0
  79. package/dist/io/version-compare.js.map +1 -1
  80. package/dist/policy-packs/builtin/solution-acceptance-reconnect.d.ts +50 -0
  81. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js +151 -0
  82. package/dist/policy-packs/builtin/solution-acceptance-reconnect.js.map +1 -0
  83. package/dist/policy-packs/builtin/solution-acceptance-runtime.d.ts +43 -4
  84. package/dist/policy-packs/builtin/solution-acceptance-runtime.js +43 -39
  85. package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
  86. package/dist/policy-packs/builtin/solution-acceptance.js +4 -2
  87. package/dist/policy-packs/builtin/solution-acceptance.js.map +1 -1
  88. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js +15 -0
  89. package/dist/policy-packs/builtin/understanding-before-execution/delegation-markers.js.map +1 -1
  90. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js +5 -7
  91. package/dist/policy-packs/builtin/understanding-before-execution/inflight-records.js.map +1 -1
  92. package/dist/policy-packs/builtin/understanding-before-execution/markers.js +23 -0
  93. package/dist/policy-packs/builtin/understanding-before-execution/markers.js.map +1 -1
  94. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js +8 -17
  95. package/dist/policy-packs/builtin/understanding-before-execution/post-tool-use-boundary.js.map +1 -1
  96. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.d.ts +129 -0
  97. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js +166 -0
  98. package/dist/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.js.map +1 -0
  99. package/dist/policy-packs/builtin/understanding-before-execution.d.ts +129 -0
  100. package/dist/policy-packs/builtin/understanding-before-execution.js +44 -57
  101. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  102. package/dist/policy-packs/version-check.d.ts +8 -1
  103. package/dist/policy-packs/version-check.js +10 -6
  104. package/dist/policy-packs/version-check.js.map +1 -1
  105. package/dist/probes/memory.d.ts +34 -0
  106. package/dist/probes/memory.js +87 -14
  107. package/dist/probes/memory.js.map +1 -1
  108. package/dist/runtime/git-context.d.ts +125 -0
  109. package/dist/runtime/git-context.js +164 -1
  110. package/dist/runtime/git-context.js.map +1 -1
  111. package/dist/runtime/index.d.ts +1 -1
  112. package/dist/runtime/index.js +1 -1
  113. package/dist/runtime/index.js.map +1 -1
  114. package/dist/runtime/task-providers/agent-tasks.d.ts +90 -0
  115. package/dist/runtime/task-providers/agent-tasks.js +231 -0
  116. package/dist/runtime/task-providers/agent-tasks.js.map +1 -0
  117. package/dist/runtime/workflow-policies.d.ts +1 -0
  118. package/dist/runtime/workflow-policies.js +8 -8
  119. package/dist/runtime/workflow-policies.js.map +1 -1
  120. package/dist/schema/index.d.ts +19 -0
  121. package/dist/schema/index.js +5 -0
  122. package/dist/schema/index.js.map +1 -1
  123. package/dist/schema/session-start-preflight.d.ts +10 -0
  124. package/dist/schema/session-start-preflight.js +106 -0
  125. package/dist/schema/session-start-preflight.js.map +1 -0
  126. package/package.json +7 -2
@@ -0,0 +1,129 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The optional reminder integration's one shared strict input schema.
4
+ * Runtime consumers deliberately parse this schema again: a previously
5
+ * generated hook must turn into a no-op when an operator disables or breaks
6
+ * the current manifest after apply.
7
+ */
8
+ export declare const StayInScopeConfigSchema: z.ZodEffects<z.ZodObject<{
9
+ enabled: z.ZodBoolean;
10
+ tools: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
11
+ label_markers: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
12
+ description_markers: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
13
+ description_window: z.ZodOptional<z.ZodNullable<z.ZodObject<{
14
+ marker: z.ZodEffects<z.ZodString, string, string>;
15
+ contains: z.ZodEffects<z.ZodString, string, string>;
16
+ max_chars: z.ZodNumber;
17
+ }, "strict", z.ZodTypeAny, {
18
+ marker: string;
19
+ contains: string;
20
+ max_chars: number;
21
+ }, {
22
+ marker: string;
23
+ contains: string;
24
+ max_chars: number;
25
+ }>>>;
26
+ parent_reference_pattern: z.ZodOptional<z.ZodEffects<z.ZodNullable<z.ZodString>, string | null, string | null>>;
27
+ parent_url_pattern: z.ZodOptional<z.ZodEffects<z.ZodNullable<z.ZodString>, string | null, string | null>>;
28
+ messages: z.ZodOptional<z.ZodObject<{
29
+ reminder: z.ZodEffects<z.ZodString, string, string>;
30
+ second_order: z.ZodEffects<z.ZodString, string, string>;
31
+ }, "strict", z.ZodTypeAny, {
32
+ reminder: string;
33
+ second_order: string;
34
+ }, {
35
+ reminder: string;
36
+ second_order: string;
37
+ }>>;
38
+ }, "strict", z.ZodTypeAny, {
39
+ enabled: boolean;
40
+ tools?: string[] | undefined;
41
+ label_markers?: string[] | undefined;
42
+ description_markers?: string[] | undefined;
43
+ description_window?: {
44
+ marker: string;
45
+ contains: string;
46
+ max_chars: number;
47
+ } | null | undefined;
48
+ parent_reference_pattern?: string | null | undefined;
49
+ parent_url_pattern?: string | null | undefined;
50
+ messages?: {
51
+ reminder: string;
52
+ second_order: string;
53
+ } | undefined;
54
+ }, {
55
+ enabled: boolean;
56
+ tools?: string[] | undefined;
57
+ label_markers?: string[] | undefined;
58
+ description_markers?: string[] | undefined;
59
+ description_window?: {
60
+ marker: string;
61
+ contains: string;
62
+ max_chars: number;
63
+ } | null | undefined;
64
+ parent_reference_pattern?: string | null | undefined;
65
+ parent_url_pattern?: string | null | undefined;
66
+ messages?: {
67
+ reminder: string;
68
+ second_order: string;
69
+ } | undefined;
70
+ }>, {
71
+ enabled: boolean;
72
+ tools?: string[] | undefined;
73
+ label_markers?: string[] | undefined;
74
+ description_markers?: string[] | undefined;
75
+ description_window?: {
76
+ marker: string;
77
+ contains: string;
78
+ max_chars: number;
79
+ } | null | undefined;
80
+ parent_reference_pattern?: string | null | undefined;
81
+ parent_url_pattern?: string | null | undefined;
82
+ messages?: {
83
+ reminder: string;
84
+ second_order: string;
85
+ } | undefined;
86
+ }, {
87
+ enabled: boolean;
88
+ tools?: string[] | undefined;
89
+ label_markers?: string[] | undefined;
90
+ description_markers?: string[] | undefined;
91
+ description_window?: {
92
+ marker: string;
93
+ contains: string;
94
+ max_chars: number;
95
+ } | null | undefined;
96
+ parent_reference_pattern?: string | null | undefined;
97
+ parent_url_pattern?: string | null | undefined;
98
+ messages?: {
99
+ reminder: string;
100
+ second_order: string;
101
+ } | undefined;
102
+ }>;
103
+ export interface StayInScopeConfig {
104
+ tools: readonly string[];
105
+ labelMarkers: readonly string[];
106
+ descriptionMarkers: readonly string[];
107
+ descriptionWindow: {
108
+ marker: string;
109
+ contains: string;
110
+ maxChars: number;
111
+ } | null;
112
+ parentReferencePattern: string | null;
113
+ parentUrlPattern: string | null;
114
+ messages: {
115
+ reminder: string;
116
+ secondOrder: string;
117
+ };
118
+ }
119
+ /** Returns null for absent, disabled, or invalid configuration. */
120
+ export declare function resolveStayInScopeConfig(raw: unknown): StayInScopeConfig | null;
121
+ export type StayInScopeMatchedRule = "label" | "hintergrund-marker" | "explicit-marker" | "none";
122
+ export interface StayInScopeMatchEvaluation {
123
+ matched: boolean;
124
+ matchedRule: StayInScopeMatchedRule;
125
+ secondOrder: boolean;
126
+ parentReference: string | null;
127
+ }
128
+ export declare function evaluateStayInScopeMatch(config: StayInScopeConfig, labels: readonly string[], description: string): StayInScopeMatchEvaluation;
129
+ export declare function extractConfiguredParentUrl(config: StayInScopeConfig, description: string, parentReference: string | null): string | null;
@@ -0,0 +1,166 @@
1
+ import { z } from "zod";
2
+ const NonblankString = z.string().min(1).refine((value) => value.trim().length > 0, {
3
+ message: "must not be blank",
4
+ });
5
+ // Codex expands only a bare pipe-list of simple names. Keeping the same
6
+ // restricted grammar here means a configured name cannot change matcher
7
+ // syntax or introduce a regular-expression fragment.
8
+ const SimpleToolName = z.string().min(1).regex(/^[A-Za-z0-9_.:-]+$/, {
9
+ message: "must be a simple tool name without matcher metacharacters",
10
+ }).refine((value) => value.trim().length > 0, { message: "must not be blank" });
11
+ const DescriptionWindowSchema = z
12
+ .object({
13
+ marker: NonblankString,
14
+ contains: NonblankString,
15
+ max_chars: z.number().int().positive(),
16
+ })
17
+ .strict();
18
+ const ParentReferencePattern = z.string().nullable().superRefine((value, ctx) => {
19
+ if (value === null)
20
+ return;
21
+ try {
22
+ new RegExp(value, "i");
23
+ }
24
+ catch {
25
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must be valid regular-expression source" });
26
+ }
27
+ });
28
+ const ParentUrlPattern = z.string().nullable().superRefine((value, ctx) => {
29
+ if (value === null)
30
+ return;
31
+ try {
32
+ new RegExp(value);
33
+ }
34
+ catch {
35
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: "must be valid regular-expression source" });
36
+ }
37
+ });
38
+ /**
39
+ * The optional reminder integration's one shared strict input schema.
40
+ * Runtime consumers deliberately parse this schema again: a previously
41
+ * generated hook must turn into a no-op when an operator disables or breaks
42
+ * the current manifest after apply.
43
+ */
44
+ export const StayInScopeConfigSchema = z
45
+ .object({
46
+ enabled: z.boolean(),
47
+ tools: z.array(SimpleToolName).min(1).optional(),
48
+ label_markers: z.array(NonblankString).optional(),
49
+ description_markers: z.array(NonblankString).optional(),
50
+ description_window: DescriptionWindowSchema.nullable().optional(),
51
+ parent_reference_pattern: ParentReferencePattern.optional(),
52
+ parent_url_pattern: ParentUrlPattern.optional(),
53
+ messages: z
54
+ .object({ reminder: NonblankString, second_order: NonblankString })
55
+ .strict()
56
+ .optional(),
57
+ })
58
+ .strict()
59
+ .superRefine((value, ctx) => {
60
+ if (!value.enabled)
61
+ return;
62
+ for (const key of [
63
+ "tools",
64
+ "label_markers",
65
+ "description_markers",
66
+ "description_window",
67
+ "parent_reference_pattern",
68
+ "parent_url_pattern",
69
+ "messages",
70
+ ]) {
71
+ if (value[key] === undefined) {
72
+ ctx.addIssue({ code: z.ZodIssueCode.custom, path: [key], message: "is required when enabled:true" });
73
+ }
74
+ }
75
+ if ((value.label_markers?.length ?? 0) === 0 &&
76
+ (value.description_markers?.length ?? 0) === 0 &&
77
+ value.description_window === null) {
78
+ ctx.addIssue({
79
+ code: z.ZodIssueCode.custom,
80
+ message: "enabled:true requires at least one label, description, or window detector",
81
+ });
82
+ }
83
+ });
84
+ /** Returns null for absent, disabled, or invalid configuration. */
85
+ export function resolveStayInScopeConfig(raw) {
86
+ const parsed = StayInScopeConfigSchema.safeParse(raw);
87
+ if (!parsed.success || !parsed.data.enabled)
88
+ return null;
89
+ const value = parsed.data;
90
+ // superRefine establishes these fields for enabled:true. The narrow casts
91
+ // keep the public resolver free of a second, diverging validation path.
92
+ if (value.tools === undefined ||
93
+ value.label_markers === undefined ||
94
+ value.description_markers === undefined ||
95
+ value.description_window === undefined ||
96
+ value.parent_reference_pattern === undefined ||
97
+ value.parent_url_pattern === undefined ||
98
+ value.messages === undefined) {
99
+ return null;
100
+ }
101
+ return {
102
+ tools: value.tools,
103
+ labelMarkers: value.label_markers,
104
+ descriptionMarkers: value.description_markers,
105
+ descriptionWindow: value.description_window === null
106
+ ? null
107
+ : {
108
+ marker: value.description_window.marker,
109
+ contains: value.description_window.contains,
110
+ maxChars: value.description_window.max_chars,
111
+ },
112
+ parentReferencePattern: value.parent_reference_pattern,
113
+ parentUrlPattern: value.parent_url_pattern,
114
+ messages: {
115
+ reminder: value.messages.reminder,
116
+ secondOrder: value.messages.second_order,
117
+ },
118
+ };
119
+ }
120
+ function firstParentReference(description, pattern) {
121
+ if (pattern === null)
122
+ return null;
123
+ const match = description.match(new RegExp(pattern, "i"));
124
+ const capture = match?.[1];
125
+ return typeof capture === "string" && /^[0-9]+$/.test(capture) ? capture : null;
126
+ }
127
+ export function evaluateStayInScopeMatch(config, labels, description) {
128
+ const labelMatch = labels.some((label) => config.labelMarkers.some((marker) => label.toLowerCase().includes(marker.toLowerCase())));
129
+ const explicitMarker = config.descriptionMarkers.some((marker) => description.includes(marker));
130
+ const window = config.descriptionWindow;
131
+ const windowMatch = !explicitMarker &&
132
+ window !== null &&
133
+ (() => {
134
+ const index = description.indexOf(window.marker);
135
+ return index !== -1 && description.slice(index, index + window.maxChars).toLowerCase().includes(window.contains.toLowerCase());
136
+ })();
137
+ const matchedRule = labelMatch
138
+ ? "label"
139
+ : explicitMarker
140
+ ? "explicit-marker"
141
+ : windowMatch
142
+ ? "hintergrund-marker"
143
+ : "none";
144
+ const parentReference = firstParentReference(description, config.parentReferencePattern);
145
+ return {
146
+ matched: matchedRule !== "none",
147
+ matchedRule,
148
+ secondOrder: labelMatch && parentReference !== null,
149
+ parentReference,
150
+ };
151
+ }
152
+ export function extractConfiguredParentUrl(config, description, parentReference) {
153
+ if (config.parentUrlPattern !== null) {
154
+ const pattern = new RegExp(config.parentUrlPattern, "g");
155
+ let match;
156
+ while ((match = pattern.exec(description)) !== null) {
157
+ if (match[0].length > 0)
158
+ return match[0];
159
+ if (pattern.lastIndex >= description.length)
160
+ break;
161
+ pattern.lastIndex += 1;
162
+ }
163
+ }
164
+ return parentReference === null ? null : `#${parentReference}`;
165
+ }
166
+ //# sourceMappingURL=stay-in-scope-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stay-in-scope-config.js","sourceRoot":"","sources":["../../../../src/policy-packs/builtin/understanding-before-execution/stay-in-scope-config.ts"],"names":[],"mappings":"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"}
@@ -202,6 +202,101 @@ export declare const configSchema: z.ZodObject<{
202
202
  description: string;
203
203
  kind: "ask";
204
204
  }>]>, "many">>;
205
+ stay_in_scope: z.ZodOptional<z.ZodEffects<z.ZodObject<{
206
+ enabled: z.ZodBoolean;
207
+ tools: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
208
+ label_markers: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
209
+ description_markers: z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>;
210
+ description_window: z.ZodOptional<z.ZodNullable<z.ZodObject<{
211
+ marker: z.ZodEffects<z.ZodString, string, string>;
212
+ contains: z.ZodEffects<z.ZodString, string, string>;
213
+ max_chars: z.ZodNumber;
214
+ }, "strict", z.ZodTypeAny, {
215
+ marker: string;
216
+ contains: string;
217
+ max_chars: number;
218
+ }, {
219
+ marker: string;
220
+ contains: string;
221
+ max_chars: number;
222
+ }>>>;
223
+ parent_reference_pattern: z.ZodOptional<z.ZodEffects<z.ZodNullable<z.ZodString>, string | null, string | null>>;
224
+ parent_url_pattern: z.ZodOptional<z.ZodEffects<z.ZodNullable<z.ZodString>, string | null, string | null>>;
225
+ messages: z.ZodOptional<z.ZodObject<{
226
+ reminder: z.ZodEffects<z.ZodString, string, string>;
227
+ second_order: z.ZodEffects<z.ZodString, string, string>;
228
+ }, "strict", z.ZodTypeAny, {
229
+ reminder: string;
230
+ second_order: string;
231
+ }, {
232
+ reminder: string;
233
+ second_order: string;
234
+ }>>;
235
+ }, "strict", z.ZodTypeAny, {
236
+ enabled: boolean;
237
+ tools?: string[] | undefined;
238
+ label_markers?: string[] | undefined;
239
+ description_markers?: string[] | undefined;
240
+ description_window?: {
241
+ marker: string;
242
+ contains: string;
243
+ max_chars: number;
244
+ } | null | undefined;
245
+ parent_reference_pattern?: string | null | undefined;
246
+ parent_url_pattern?: string | null | undefined;
247
+ messages?: {
248
+ reminder: string;
249
+ second_order: string;
250
+ } | undefined;
251
+ }, {
252
+ enabled: boolean;
253
+ tools?: string[] | undefined;
254
+ label_markers?: string[] | undefined;
255
+ description_markers?: string[] | undefined;
256
+ description_window?: {
257
+ marker: string;
258
+ contains: string;
259
+ max_chars: number;
260
+ } | null | undefined;
261
+ parent_reference_pattern?: string | null | undefined;
262
+ parent_url_pattern?: string | null | undefined;
263
+ messages?: {
264
+ reminder: string;
265
+ second_order: string;
266
+ } | undefined;
267
+ }>, {
268
+ enabled: boolean;
269
+ tools?: string[] | undefined;
270
+ label_markers?: string[] | undefined;
271
+ description_markers?: string[] | undefined;
272
+ description_window?: {
273
+ marker: string;
274
+ contains: string;
275
+ max_chars: number;
276
+ } | null | undefined;
277
+ parent_reference_pattern?: string | null | undefined;
278
+ parent_url_pattern?: string | null | undefined;
279
+ messages?: {
280
+ reminder: string;
281
+ second_order: string;
282
+ } | undefined;
283
+ }, {
284
+ enabled: boolean;
285
+ tools?: string[] | undefined;
286
+ label_markers?: string[] | undefined;
287
+ description_markers?: string[] | undefined;
288
+ description_window?: {
289
+ marker: string;
290
+ contains: string;
291
+ max_chars: number;
292
+ } | null | undefined;
293
+ parent_reference_pattern?: string | null | undefined;
294
+ parent_url_pattern?: string | null | undefined;
295
+ messages?: {
296
+ reminder: string;
297
+ second_order: string;
298
+ } | undefined;
299
+ }>>;
205
300
  }, "strict", z.ZodTypeAny, {
206
301
  producers?: ({
207
302
  command: string;
@@ -238,6 +333,23 @@ export declare const configSchema: z.ZodObject<{
238
333
  max_wait: string;
239
334
  } | undefined;
240
335
  } | undefined;
336
+ stay_in_scope?: {
337
+ enabled: boolean;
338
+ tools?: string[] | undefined;
339
+ label_markers?: string[] | undefined;
340
+ description_markers?: string[] | undefined;
341
+ description_window?: {
342
+ marker: string;
343
+ contains: string;
344
+ max_chars: number;
345
+ } | null | undefined;
346
+ parent_reference_pattern?: string | null | undefined;
347
+ parent_url_pattern?: string | null | undefined;
348
+ messages?: {
349
+ reminder: string;
350
+ second_order: string;
351
+ } | undefined;
352
+ } | undefined;
241
353
  }, {
242
354
  producers?: ({
243
355
  command: string;
@@ -274,6 +386,23 @@ export declare const configSchema: z.ZodObject<{
274
386
  max_wait: string;
275
387
  } | undefined;
276
388
  } | undefined;
389
+ stay_in_scope?: {
390
+ enabled: boolean;
391
+ tools?: string[] | undefined;
392
+ label_markers?: string[] | undefined;
393
+ description_markers?: string[] | undefined;
394
+ description_window?: {
395
+ marker: string;
396
+ contains: string;
397
+ max_chars: number;
398
+ } | null | undefined;
399
+ parent_reference_pattern?: string | null | undefined;
400
+ parent_url_pattern?: string | null | undefined;
401
+ messages?: {
402
+ reminder: string;
403
+ second_order: string;
404
+ } | undefined;
405
+ } | undefined;
277
406
  }>;
278
407
  export interface ResolvePackOptions {
279
408
  /**
@@ -27,6 +27,8 @@ import { AUTO_APPROVE_HARNESS_VALUES, REPORTS_DIR_ENV, } from "./understanding-b
27
27
  // which this file only names in comments and so does not import.
28
28
  import { parseReportScanMaxWait } from "./understanding-before-execution/index.js";
29
29
  import { SHELL_ALIASES } from "../../runtime/tool-name-aliases.js";
30
+ import { ACTIVE_CLAIM_TOOL_NAMES, DEFAULT_BOUNDARY_TOOL_NAMES, } from "../../runtime/task-providers/agent-tasks.js";
31
+ import { resolveStayInScopeConfig, StayInScopeConfigSchema, } from "./understanding-before-execution/stay-in-scope-config.js";
30
32
  // Env var the npm-backed `understanding-gate-claude-hook` bin reads to find
31
33
  // the harness pause sentinel (`src/runtime/pause-sentinel.ts`,
32
34
  // SENTINEL_BASENAME = ".harness-paused"). The bin runs OUTSIDE harness's
@@ -303,6 +305,7 @@ export const configSchema = z
303
305
  // policy-layer `ux:` / `producers:` keys.
304
306
  ux: PolicyUxSchema.optional(),
305
307
  producers: z.array(ProducerSchema).min(1).optional(),
308
+ stay_in_scope: StayInScopeConfigSchema.optional(),
306
309
  })
307
310
  .strict();
308
311
  /**
@@ -406,16 +409,7 @@ export function toPackageMode(mode) {
406
409
  // other task systems override the list in their manifest. Kept here so
407
410
  // the PostToolUse hook always emits a sensible match pattern even when
408
411
  // the operator hasn't set the config explicitly.
409
- const DEFAULT_EXPIRE_ON_TOOL_MATCH = [
410
- "mcp__agent-tasks__task_finish",
411
- "mcp__agent-tasks__task_abandon",
412
- "mcp__agent-tasks__pull_requests_merge",
413
- // Legacy v1 verb; the post-tool-use hook applies a status filter so only
414
- // `tool_input.status === "done"` actually clears the marker (PR #200,
415
- // agent-tasks 9e06175f). Listed here so settings.json fires the hook
416
- // at all; the status refinement happens in TypeScript.
417
- "mcp__agent-tasks__tasks_transition",
418
- ];
412
+ const DEFAULT_EXPIRE_ON_TOOL_MATCH = DEFAULT_BOUNDARY_TOOL_NAMES;
419
413
  const POST_TOOL_USE_COMMAND_CLAUDE = "harness pack hook post-tool-use";
420
414
  // Slice 2 of the subagent-gate work (docs/decisions/2026-08-27-ug-auto-
421
415
  // mode-approval.md "TTL, cwd, and subagents"): Claude-only for now (an
@@ -441,7 +435,7 @@ const TRACK_ACTIVE_CLAIM_COMMAND = "harness pack hook track-active-claim";
441
435
  // Agent-tasks specific; operators on other tasking systems can ignore
442
436
  // this hook (the matcher won't fire for their tools). A config-driven
443
437
  // extension can land later if a second tasking system asks for it.
444
- const TRACK_ACTIVE_CLAIM_MATCH = "^(?:mcp__agent-tasks__task_start|mcp__agent-tasks__task_finish|mcp__agent-tasks__task_abandon|mcp__agent-tasks__tasks_transition)$";
438
+ const TRACK_ACTIVE_CLAIM_MATCH = `^(?:${ACTIVE_CLAIM_TOOL_NAMES.join("|")})$`;
445
439
  // Codex sibling of TRACK_ACTIVE_CLAIM_MATCH above (task cf4cdc93): a
446
440
  // bare, unescaped `|`-joined list, NOT the anchored `^(?:...)$` form —
447
441
  // same rationale as `codexPostToolUseMatchPattern`'s own doc comment
@@ -454,17 +448,11 @@ const TRACK_ACTIVE_CLAIM_MATCH = "^(?:mcp__agent-tasks__task_start|mcp__agent-ta
454
448
  // even reach the hook. This bare form lets the emitted `config.toml`
455
449
  // matcher — and therefore Codex's own dispatcher — recognize those
456
450
  // variants, exactly like `PRE_TOOL_USE_MATCH_CODEX` already does.
457
- const TRACK_ACTIVE_CLAIM_MATCH_CODEX = "mcp__agent-tasks__task_start|mcp__agent-tasks__task_finish|mcp__agent-tasks__task_abandon|mcp__agent-tasks__tasks_transition";
458
- // Hardcoded matcher for the stay-in-scope reminder (harness/2ba06030).
459
- // Fires on the three task-mutation verbs that can carry labels and
460
- // description fields. tasks_update is included so a label added
461
- // post-hoc (e.g. `tasks_update { labels: ["from-review"] }`) still
462
- // surfaces the reminder.
463
- const STAY_IN_SCOPE_MATCH = "^(?:mcp__agent-tasks__task_create|mcp__agent-tasks__tasks_create|mcp__agent-tasks__tasks_update)$";
464
- // Codex sibling of STAY_IN_SCOPE_MATCH above (task cf4cdc93), same
465
- // bare-unanchored-list rationale as TRACK_ACTIVE_CLAIM_MATCH_CODEX.
466
- const STAY_IN_SCOPE_MATCH_CODEX = "mcp__agent-tasks__task_create|mcp__agent-tasks__tasks_create|mcp__agent-tasks__tasks_update";
451
+ const TRACK_ACTIVE_CLAIM_MATCH_CODEX = ACTIVE_CLAIM_TOOL_NAMES.join("|");
467
452
  const STAY_IN_SCOPE_COMMAND = "harness pack hook stay-in-scope";
453
+ function escapeMatcherLiteral(value) {
454
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
455
+ }
468
456
  // Bash tool name used to widen the PostToolUse matcher when
469
457
  // `expire_on_bash_match` is configured (task bea04a03). Matches the
470
458
  // single entry of `DEFAULT_BASH_TOOL_NAMES` in
@@ -758,20 +746,22 @@ function buildHooks(runtime, pack, mode, opts = {}) {
758
746
  command: TRACK_ACTIVE_CLAIM_COMMAND,
759
747
  blocking: false,
760
748
  budget_ms: 2000,
761
- description: "Codex adapter: track the active agent-tasks claim by writing/clearing <generatedDir>/active-claim on task_start / task_finish / task_abandon. Lets `harness approve understanding` auto-resolve the task id (harness/494fd1e5, Codex parity task cf4cdc93).",
762
- },
763
- // Stay-in-scope reminder (harness/2ba06030), Codex parity task
764
- // cf4cdc93 (parity-gaps doc gap #3). Same always-on / soft-only
765
- // semantics as the Claude sibling below.
766
- {
767
- name: `${HOOK_NAME_PREFIX}:codex:stay-in-scope`,
768
- event: "PostToolUse",
769
- match: STAY_IN_SCOPE_MATCH_CODEX,
770
- command: STAY_IN_SCOPE_COMMAND,
771
- blocking: false,
772
- budget_ms: 2000,
773
- description: "Codex adapter: emit a soft reminder + audit row when a review-derived follow-up task gets created. Surfaces user-memory feedback_reviewer_findings_stay_in_scope. Disable: STAY_IN_SCOPE_DISABLED=1 (harness/2ba06030, Codex parity task cf4cdc93).",
749
+ description: "Codex adapter: track the active agent-tasks claim by writing/clearing <generatedDir>/active-claim on task_start / task_finish / task_abandon / task_merge. Lets `harness approve understanding` auto-resolve the task id (harness/494fd1e5, Codex parity task cf4cdc93).",
774
750
  },
751
+ ...(() => {
752
+ const stayInScope = resolveStayInScopeConfig(pack.config["stay_in_scope"]);
753
+ if (stayInScope === null)
754
+ return [];
755
+ return [{
756
+ name: `${HOOK_NAME_PREFIX}:codex:stay-in-scope`,
757
+ event: "PostToolUse",
758
+ match: stayInScope.tools.join("|"),
759
+ command: STAY_IN_SCOPE_COMMAND,
760
+ blocking: false,
761
+ budget_ms: 2000,
762
+ description: "Codex adapter: emit the configured soft stay-in-scope reminder and audit row.",
763
+ }];
764
+ })(),
775
765
  ];
776
766
  }
777
767
  // `min_version` floor on the npm-backed bins: 0.4.0 ships the
@@ -895,9 +885,11 @@ function buildHooks(runtime, pack, mode, opts = {}) {
895
885
  return [hook];
896
886
  })(),
897
887
  // Active-claim tracker (harness/494fd1e5). PostToolUse hook on
898
- // agent-tasks task_start / task_finish / task_abandon. Maintains a
899
- // small file at <generatedDir>/active-claim so `harness approve
900
- // understanding` can auto-resolve the task id when --task is
888
+ // agent-tasks task_start / task_finish / task_abandon / task_merge
889
+ // (task_merge added by task c86e3c4a: previously untracked here, so
890
+ // a real task_merge tool call never dispatched to the hook at all).
891
+ // Maintains a small file at <generatedDir>/active-claim so `harness
892
+ // approve understanding` can auto-resolve the task id when --task is
901
893
  // absent. Always emitted alongside the pack — operators on other
902
894
  // tasking systems are unaffected (the matcher won't fire for
903
895
  // their tools), the file simply never appears.
@@ -908,27 +900,22 @@ function buildHooks(runtime, pack, mode, opts = {}) {
908
900
  command: TRACK_ACTIVE_CLAIM_COMMAND,
909
901
  blocking: false,
910
902
  budget_ms: 2000,
911
- description: "Track the active agent-tasks claim by writing/clearing <generatedDir>/active-claim on task_start / task_finish / task_abandon. Lets `harness approve understanding` auto-resolve the task id (harness/494fd1e5).",
912
- },
913
- // Stay-in-scope reminder (harness/2ba06030). PostToolUse hook on
914
- // agent-tasks task_create / tasks_create / tasks_update. Emits a
915
- // one-line stderr reminder + JSONL audit row when the new task's
916
- // labels or description suggest it was carved out of a review
917
- // finding that may have been inline-fixable. Soft (no block);
918
- // enforces user-memory feedback_reviewer_findings_stay_in_scope.
919
- // Bundled with this pack for operational convenience — operators
920
- // on non-agent-tasks tasking systems are unaffected (matcher
921
- // won't fire). Disable via STAY_IN_SCOPE_DISABLED=1 in the hook's
922
- // env; override audit log path via STAY_IN_SCOPE_LOG.
923
- {
924
- name: `${HOOK_NAME_PREFIX}:stay-in-scope`,
925
- event: "PostToolUse",
926
- match: STAY_IN_SCOPE_MATCH,
927
- command: STAY_IN_SCOPE_COMMAND,
928
- blocking: false,
929
- budget_ms: 2000,
930
- description: "Emit a soft reminder + audit row when a review-derived follow-up task gets created. Surfaces user-memory feedback_reviewer_findings_stay_in_scope. Disable: STAY_IN_SCOPE_DISABLED=1.",
903
+ description: "Track the active agent-tasks claim by writing/clearing <generatedDir>/active-claim on task_start / task_finish / task_abandon / task_merge. Lets `harness approve understanding` auto-resolve the task id (harness/494fd1e5).",
931
904
  },
905
+ ...(() => {
906
+ const stayInScope = resolveStayInScopeConfig(pack.config["stay_in_scope"]);
907
+ if (stayInScope === null)
908
+ return [];
909
+ return [{
910
+ name: `${HOOK_NAME_PREFIX}:stay-in-scope`,
911
+ event: "PostToolUse",
912
+ match: `^(?:${stayInScope.tools.map(escapeMatcherLiteral).join("|")})$`,
913
+ command: STAY_IN_SCOPE_COMMAND,
914
+ blocking: false,
915
+ budget_ms: 2000,
916
+ description: "Emit the configured soft stay-in-scope reminder and audit row.",
917
+ }];
918
+ })(),
932
919
  ];
933
920
  }
934
921
  function modeFriction(mode) {