@deftai/directive-core 0.95.0 → 0.97.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/dist/cache/archive.d.ts +134 -0
- package/dist/cache/archive.js +636 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +1 -0
- package/dist/cache/main.js +298 -1
- package/dist/check/gate-lists.js +8 -0
- package/dist/consumer-check-contract/evaluate.d.ts +124 -0
- package/dist/consumer-check-contract/evaluate.js +699 -0
- package/dist/consumer-check-contract/index.d.ts +5 -0
- package/dist/consumer-check-contract/index.js +5 -0
- package/dist/content-contracts/skills/helpers.d.ts +1 -1
- package/dist/content-contracts/skills/helpers.js +1 -0
- package/dist/delivery-attempt/disk-begin.d.ts +51 -0
- package/dist/delivery-attempt/disk-begin.js +68 -0
- package/dist/delivery-attempt/evaluate.d.ts +26 -0
- package/dist/delivery-attempt/evaluate.js +443 -0
- package/dist/delivery-attempt/fingerprint.d.ts +32 -0
- package/dist/delivery-attempt/fingerprint.js +100 -0
- package/dist/delivery-attempt/handoff.d.ts +25 -0
- package/dist/delivery-attempt/handoff.js +102 -0
- package/dist/delivery-attempt/index.d.ts +17 -0
- package/dist/delivery-attempt/index.js +17 -0
- package/dist/delivery-attempt/ledger.d.ts +169 -0
- package/dist/delivery-attempt/ledger.js +758 -0
- package/dist/delivery-attempt/material-delta.d.ts +38 -0
- package/dist/delivery-attempt/material-delta.js +126 -0
- package/dist/delivery-attempt/types.d.ts +210 -0
- package/dist/delivery-attempt/types.js +77 -0
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.js +12 -0
- package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
- package/dist/doctor/openclaw-soft-rebind.js +164 -0
- package/dist/hooks/dispatcher.d.ts +2 -1
- package/dist/hooks/dispatcher.js +63 -9
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/init-deposit/gitignore.js +7 -0
- package/dist/init-deposit/hygiene.d.ts +1 -1
- package/dist/init-deposit/hygiene.js +16 -4
- package/dist/init-deposit/init-deposit.js +5 -0
- package/dist/init-deposit/refresh.js +3 -0
- package/dist/init-deposit/scaffold.js +6 -5
- package/dist/parent-turn-shape/evaluate.d.ts +84 -0
- package/dist/parent-turn-shape/evaluate.js +353 -0
- package/dist/parent-turn-shape/index.d.ts +8 -0
- package/dist/parent-turn-shape/index.js +8 -0
- package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
- package/dist/pr-merge-readiness/ci-gate.js +191 -24
- package/dist/pr-merge-readiness/compute.js +10 -1
- package/dist/pr-merge-readiness/index.d.ts +2 -1
- package/dist/pr-merge-readiness/index.js +2 -1
- package/dist/pr-merge-readiness/output.js +14 -0
- package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
- package/dist/pr-merge-readiness/platform-status.js +49 -0
- package/dist/pr-watch/constants.d.ts +10 -0
- package/dist/pr-watch/constants.js +12 -1
- package/dist/pr-watch/main.js +16 -1
- package/dist/pr-watch/probe.js +13 -1
- package/dist/pr-watch/types.d.ts +3 -2
- package/dist/pr-watch/watch.js +14 -7
- package/dist/review-monitor/constants.js +3 -2
- package/dist/review-monitor/tier-detection.d.ts +7 -3
- package/dist/review-monitor/tier-detection.js +18 -1
- package/dist/review-monitor/verify.js +18 -0
- package/dist/scope/index.d.ts +2 -0
- package/dist/scope/index.js +2 -0
- package/dist/scope/main.d.ts +10 -0
- package/dist/scope/main.js +109 -24
- package/dist/scope/promote-from-issue.d.ts +49 -0
- package/dist/scope/promote-from-issue.js +367 -0
- package/dist/scope/promote-path.d.ts +39 -0
- package/dist/scope/promote-path.js +105 -0
- package/dist/scope-provenance/digest.d.ts +67 -0
- package/dist/scope-provenance/digest.js +188 -0
- package/dist/scope-provenance/evaluate.d.ts +82 -0
- package/dist/scope-provenance/evaluate.js +528 -0
- package/dist/scope-provenance/index.d.ts +6 -0
- package/dist/scope-provenance/index.js +6 -0
- package/dist/session/compact-ritual.d.ts +96 -0
- package/dist/session/compact-ritual.js +237 -0
- package/dist/session/compact-ritual.spec.d.ts +2 -0
- package/dist/session/compact-ritual.spec.js +21 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
- package/dist/swarm/routing.d.ts +4 -2
- package/dist/swarm/routing.js +26 -4
- package/dist/test-boundary/evaluate.d.ts +54 -0
- package/dist/test-boundary/evaluate.js +368 -0
- package/dist/test-boundary/index.d.ts +6 -0
- package/dist/test-boundary/index.js +6 -0
- package/dist/test-boundary/policy.d.ts +52 -0
- package/dist/test-boundary/policy.js +182 -0
- package/dist/triage/actions/index.js +62 -2
- package/dist/triage/actions/types.d.ts +8 -1
- package/dist/triage/author-filter.d.ts +51 -0
- package/dist/triage/author-filter.js +152 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +15 -1
- package/dist/triage/classify/index.d.ts +2 -2
- package/dist/triage/classify/index.js +2 -2
- package/dist/triage/classify/label-mirror.d.ts +68 -5
- package/dist/triage/classify/label-mirror.js +261 -31
- package/dist/triage/help/registry-data.d.ts +49 -38
- package/dist/triage/help/registry-data.js +115 -40
- package/dist/triage/index.d.ts +1 -0
- package/dist/triage/index.js +1 -0
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/render.d.ts +2 -0
- package/dist/triage/queue/render.js +6 -0
- package/dist/vbrief-activate/activate.js +22 -6
- package/dist/xbrief/styles.js +33 -17
- package/package.json +11 -3
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor check + --fix wire for OpenClaw soft AGENTS re-bind skill (#3171).
|
|
3
|
+
*
|
|
4
|
+
* Complements always-pins (#3001/#3008) and L2 product commands (#3064). This
|
|
5
|
+
* skill is the required durable soft post-amnesia surface for OpenClaw — not
|
|
6
|
+
* claimed via file-host PreCompact hooks alone.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { formatOpenClawSoftRebindSkillMarkdown, isManagedOpenClawSoftRebindSkill, OPENCLAW_SOFT_REBIND_SKILL_ID, } from "../session/compact-ritual.js";
|
|
12
|
+
import { assessOpenClawSoftRebindSkill, depositOpenClawSoftRebindSkill, } from "../session/openclaw-soft-rebind-deposit.js";
|
|
13
|
+
import { detectOpenClaw, listInScopeSkillsDirs } from "./openclaw-skills.js";
|
|
14
|
+
/** Stable doctor check id for JSON findings. */
|
|
15
|
+
export const OPENCLAW_SOFT_REBIND_CHECK = "openclaw-soft-agents-rebind";
|
|
16
|
+
const DOC_OPENCLAW_HOST = "docs/openclaw-agent-host.md";
|
|
17
|
+
const REMEDIATION_FIX = "deft doctor --fix";
|
|
18
|
+
function defaultIsDir(path) {
|
|
19
|
+
try {
|
|
20
|
+
return statSync(path).isDirectory();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function assessGaps(skillsDirs) {
|
|
27
|
+
const expected = formatOpenClawSoftRebindSkillMarkdown();
|
|
28
|
+
const missing = [];
|
|
29
|
+
const stale = [];
|
|
30
|
+
let present = 0;
|
|
31
|
+
for (const skillsDir of skillsDirs) {
|
|
32
|
+
const path = join(skillsDir, OPENCLAW_SOFT_REBIND_SKILL_ID, "SKILL.md");
|
|
33
|
+
if (!existsSync(path)) {
|
|
34
|
+
missing.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
let raw;
|
|
38
|
+
try {
|
|
39
|
+
raw = readFileSync(path, "utf8");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
missing.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Unmanaged content at the required slug is incomplete health, not pass
|
|
46
|
+
// (Greptile P1 #3171). Doctor --fix overwrites via deposit.
|
|
47
|
+
if (!isManagedOpenClawSoftRebindSkill(raw) || raw !== expected) {
|
|
48
|
+
stale.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
present += 1;
|
|
52
|
+
}
|
|
53
|
+
return { missing, stale, present };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Doctor check: OpenClaw soft AGENTS re-bind skill present and current.
|
|
57
|
+
* No-ops when OpenClaw is not detected. Fix mode deposits managed skill.
|
|
58
|
+
*/
|
|
59
|
+
export function runOpenClawSoftRebindCheck(sink, addFinding, options) {
|
|
60
|
+
const seams = options.seams ?? {};
|
|
61
|
+
const env = seams.openclawEnv ?? process.env;
|
|
62
|
+
const homeDirFn = seams.openclawHomeDir ?? (() => homedir());
|
|
63
|
+
const isDir = seams.isDir ?? defaultIsDir;
|
|
64
|
+
const detect = detectOpenClaw(env, { homeDir: homeDirFn(), isDir });
|
|
65
|
+
if (!detect.detected) {
|
|
66
|
+
sink.info(`${OPENCLAW_SOFT_REBIND_CHECK}: skip -- OpenClaw not detected`);
|
|
67
|
+
addFinding({
|
|
68
|
+
severity: "skip",
|
|
69
|
+
message: "OpenClaw not detected",
|
|
70
|
+
check: OPENCLAW_SOFT_REBIND_CHECK,
|
|
71
|
+
status: "skip",
|
|
72
|
+
reason: "openclaw-not-detected",
|
|
73
|
+
});
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const skillsDirs = listInScopeSkillsDirs(detect.stateDir, options.allAgents, {
|
|
77
|
+
isDir,
|
|
78
|
+
});
|
|
79
|
+
const gaps = assessGaps(skillsDirs);
|
|
80
|
+
if (gaps.missing.length === 0 && gaps.stale.length === 0) {
|
|
81
|
+
const scope = options.allAgents ? "main + workspace-* seats" : "main workspace";
|
|
82
|
+
const message = `${OPENCLAW_SOFT_REBIND_CHECK}: soft AGENTS re-bind skill present ` +
|
|
83
|
+
`(${OPENCLAW_SOFT_REBIND_SKILL_ID}) in ${scope}`;
|
|
84
|
+
sink.success(message);
|
|
85
|
+
addFinding({
|
|
86
|
+
severity: "skip",
|
|
87
|
+
message,
|
|
88
|
+
check: OPENCLAW_SOFT_REBIND_CHECK,
|
|
89
|
+
status: "present",
|
|
90
|
+
skill: OPENCLAW_SOFT_REBIND_SKILL_ID,
|
|
91
|
+
skills_dirs: skillsDirs,
|
|
92
|
+
});
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
if (options.fixMode) {
|
|
96
|
+
const depositResult = depositOpenClawSoftRebindSkill({
|
|
97
|
+
env,
|
|
98
|
+
homeDir: homeDirFn(),
|
|
99
|
+
allAgents: options.allAgents,
|
|
100
|
+
isDir,
|
|
101
|
+
skillsDirs,
|
|
102
|
+
});
|
|
103
|
+
const after = assessGaps(skillsDirs);
|
|
104
|
+
if (after.missing.length === 0 && after.stale.length === 0) {
|
|
105
|
+
const message = `${OPENCLAW_SOFT_REBIND_CHECK}: deposited soft AGENTS re-bind skill; ` +
|
|
106
|
+
`restart OpenClaw gateway or start a new session (${DOC_OPENCLAW_HOST})`;
|
|
107
|
+
sink.success(message);
|
|
108
|
+
addFinding({
|
|
109
|
+
severity: "skip",
|
|
110
|
+
message,
|
|
111
|
+
check: OPENCLAW_SOFT_REBIND_CHECK,
|
|
112
|
+
status: "fixed",
|
|
113
|
+
written: depositResult.writtenPaths,
|
|
114
|
+
});
|
|
115
|
+
return depositResult;
|
|
116
|
+
}
|
|
117
|
+
// Partial fix: report remaining gaps from post-deposit assessment (SLizard P2).
|
|
118
|
+
const partsAfter = [];
|
|
119
|
+
if (after.missing.length > 0) {
|
|
120
|
+
partsAfter.push(`missing=${after.missing.join(", ")}`);
|
|
121
|
+
}
|
|
122
|
+
if (after.stale.length > 0) {
|
|
123
|
+
partsAfter.push(`stale=${after.stale.join(", ")}`);
|
|
124
|
+
}
|
|
125
|
+
const messageAfter = `${OPENCLAW_SOFT_REBIND_CHECK}: OpenClaw soft AGENTS re-bind skill incomplete after fix ` +
|
|
126
|
+
`(${partsAfter.join("; ")}). Remediation: ${REMEDIATION_FIX} — see ${DOC_OPENCLAW_HOST}`;
|
|
127
|
+
sink.warn(messageAfter);
|
|
128
|
+
addFinding({
|
|
129
|
+
severity: "warning",
|
|
130
|
+
message: messageAfter,
|
|
131
|
+
check: OPENCLAW_SOFT_REBIND_CHECK,
|
|
132
|
+
status: "incomplete",
|
|
133
|
+
missing: after.missing,
|
|
134
|
+
stale: after.stale,
|
|
135
|
+
written: depositResult.writtenPaths,
|
|
136
|
+
suggestion: REMEDIATION_FIX,
|
|
137
|
+
docs: [DOC_OPENCLAW_HOST],
|
|
138
|
+
});
|
|
139
|
+
return depositResult;
|
|
140
|
+
}
|
|
141
|
+
const parts = [];
|
|
142
|
+
if (gaps.missing.length > 0) {
|
|
143
|
+
parts.push(`missing=${gaps.missing.join(", ")}`);
|
|
144
|
+
}
|
|
145
|
+
if (gaps.stale.length > 0) {
|
|
146
|
+
parts.push(`stale=${gaps.stale.join(", ")}`);
|
|
147
|
+
}
|
|
148
|
+
const message = `${OPENCLAW_SOFT_REBIND_CHECK}: OpenClaw soft AGENTS re-bind skill incomplete ` +
|
|
149
|
+
`(${parts.join("; ")}). Remediation: ${REMEDIATION_FIX} — see ${DOC_OPENCLAW_HOST}`;
|
|
150
|
+
sink.warn(message);
|
|
151
|
+
addFinding({
|
|
152
|
+
severity: "warning",
|
|
153
|
+
message,
|
|
154
|
+
check: OPENCLAW_SOFT_REBIND_CHECK,
|
|
155
|
+
status: "incomplete",
|
|
156
|
+
missing: gaps.missing,
|
|
157
|
+
stale: gaps.stale,
|
|
158
|
+
suggestion: REMEDIATION_FIX,
|
|
159
|
+
docs: [DOC_OPENCLAW_HOST],
|
|
160
|
+
});
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
export { assessOpenClawSoftRebindSkill, depositOpenClawSoftRebindSkill };
|
|
164
|
+
//# sourceMappingURL=openclaw-soft-rebind.js.map
|
|
@@ -140,7 +140,8 @@ export declare function decideHook(input: HookDispatchInput, seams?: HookPolicyS
|
|
|
140
140
|
* Cursor deposits use `failClosed: true`. Cursor treats empty/null stdout as a
|
|
141
141
|
* hook failure and blocks the tool — so Cursor allows must emit explicit
|
|
142
142
|
* `{"permission":"allow"}`. Other hosts keep empty allow so the host permission
|
|
143
|
-
* flow is unchanged.
|
|
143
|
+
* flow is unchanged — except session.start / session.compact soft re-bind
|
|
144
|
+
* injection (#3171), which surfaces checklist text without a write tool.
|
|
144
145
|
*
|
|
145
146
|
* Cursor stdout always includes `code` (stable machine-readable decision code)
|
|
146
147
|
* so agents can distinguish policy denials from host-integration failures
|
package/dist/hooks/dispatcher.js
CHANGED
|
@@ -7,6 +7,7 @@ import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
|
|
|
7
7
|
import { detectNoDeftDirective, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, } from "../policy/no-deft-directive.js";
|
|
8
8
|
import { classifyMcpTool, DEFAULT_RUNTIME_AUTHORITY_POLICY, evaluateRuntimeAuthorityDirectWrite, evaluateRuntimeAuthorityShellOp, listShellOps, loadRuntimeAuthorityFromProject, } from "../policy/runtime-authority.js";
|
|
9
9
|
import { loadStoryWriteFenceFromPath, resolveWriteFence } from "../policy/write-fence.js";
|
|
10
|
+
import { appendSoftAgentsRebindToMessage, decisionCarriesSoftAgentsRebind, formatSoftAgentsRebindChecklist, } from "../session/compact-ritual.js";
|
|
10
11
|
import { detectBranch } from "../session/git.js";
|
|
11
12
|
import { emitSessionRitualBlockedProcessCost } from "../session/process-cost.js";
|
|
12
13
|
import { markRitualStaleAfterCompact } from "../session/ritual-sentinel.js";
|
|
@@ -716,7 +717,8 @@ export function decideHook(input, seams = {}) {
|
|
|
716
717
|
host: input.host,
|
|
717
718
|
toolName: null,
|
|
718
719
|
projectRoot,
|
|
719
|
-
|
|
720
|
+
// Soft checklist still surfaces after compact even when ritual was absent (#3171).
|
|
721
|
+
message: appendSoftAgentsRebindToMessage(result.message),
|
|
720
722
|
scopePath: null,
|
|
721
723
|
};
|
|
722
724
|
}
|
|
@@ -727,7 +729,7 @@ export function decideHook(input, seams = {}) {
|
|
|
727
729
|
host: input.host,
|
|
728
730
|
toolName: null,
|
|
729
731
|
projectRoot,
|
|
730
|
-
message: result.message,
|
|
732
|
+
message: appendSoftAgentsRebindToMessage(result.message),
|
|
731
733
|
scopePath: null,
|
|
732
734
|
};
|
|
733
735
|
}
|
|
@@ -739,8 +741,8 @@ export function decideHook(input, seams = {}) {
|
|
|
739
741
|
host: input.host,
|
|
740
742
|
toolName: null,
|
|
741
743
|
projectRoot,
|
|
742
|
-
message: "Directive compact re-arm bookkeeping failed on its non-blocking path: " +
|
|
743
|
-
`${String(cause)}
|
|
744
|
+
message: appendSoftAgentsRebindToMessage("Directive compact re-arm bookkeeping failed on its non-blocking path: " +
|
|
745
|
+
`${String(cause)}`),
|
|
744
746
|
scopePath: null,
|
|
745
747
|
};
|
|
746
748
|
}
|
|
@@ -775,8 +777,9 @@ export function decideHook(input, seams = {}) {
|
|
|
775
777
|
host: input.host,
|
|
776
778
|
toolName: null,
|
|
777
779
|
projectRoot,
|
|
778
|
-
|
|
779
|
-
|
|
780
|
+
// Best-effort soft cue on SessionStart (Codex gap + resume/amnesia) (#3171).
|
|
781
|
+
message: appendSoftAgentsRebindToMessage(`Directive SessionStart bookkeeping reported exit ${result.code} on its non-blocking path` +
|
|
782
|
+
`${detail.length > 0 ? `: ${detail}` : "."}`),
|
|
780
783
|
scopePath: null,
|
|
781
784
|
};
|
|
782
785
|
}
|
|
@@ -789,7 +792,7 @@ export function decideHook(input, seams = {}) {
|
|
|
789
792
|
host: input.host,
|
|
790
793
|
toolName: null,
|
|
791
794
|
projectRoot,
|
|
792
|
-
message: `Directive SessionStart bookkeeping failed on its non-blocking path: ${String(cause)}
|
|
795
|
+
message: appendSoftAgentsRebindToMessage(`Directive SessionStart bookkeeping failed on its non-blocking path: ${String(cause)}`),
|
|
793
796
|
scopePath: null,
|
|
794
797
|
};
|
|
795
798
|
}
|
|
@@ -800,7 +803,8 @@ export function decideHook(input, seams = {}) {
|
|
|
800
803
|
host: input.host,
|
|
801
804
|
toolName: null,
|
|
802
805
|
projectRoot,
|
|
803
|
-
|
|
806
|
+
// Soft AGENTS re-bind on SessionStart without requiring a write tool (#3171 / #2769).
|
|
807
|
+
message: appendSoftAgentsRebindToMessage("SessionStart bookkeeping completed on a non-blocking path."),
|
|
804
808
|
scopePath: null,
|
|
805
809
|
};
|
|
806
810
|
}
|
|
@@ -881,13 +885,27 @@ export function decideHook(input, seams = {}) {
|
|
|
881
885
|
}
|
|
882
886
|
return inspectMutationGates(input, toolName, seams, { proposedLifecycleExempt: true });
|
|
883
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* Soft AGENTS re-bind text for host injection when decision carries soft path (#3171).
|
|
890
|
+
* Prefer the checklist portion of the decision message; fall back to SoT formatter.
|
|
891
|
+
*/
|
|
892
|
+
function softAgentsRebindWireText(decision) {
|
|
893
|
+
if (!decisionCarriesSoftAgentsRebind({ event: decision.event, code: decision.code })) {
|
|
894
|
+
return null;
|
|
895
|
+
}
|
|
896
|
+
if (decision.message.includes("Soft AGENTS re-bind checklist:")) {
|
|
897
|
+
return decision.message;
|
|
898
|
+
}
|
|
899
|
+
return formatSoftAgentsRebindChecklist();
|
|
900
|
+
}
|
|
884
901
|
/**
|
|
885
902
|
* Render host-facing hook output.
|
|
886
903
|
*
|
|
887
904
|
* Cursor deposits use `failClosed: true`. Cursor treats empty/null stdout as a
|
|
888
905
|
* hook failure and blocks the tool — so Cursor allows must emit explicit
|
|
889
906
|
* `{"permission":"allow"}`. Other hosts keep empty allow so the host permission
|
|
890
|
-
* flow is unchanged.
|
|
907
|
+
* flow is unchanged — except session.start / session.compact soft re-bind
|
|
908
|
+
* injection (#3171), which surfaces checklist text without a write tool.
|
|
891
909
|
*
|
|
892
910
|
* Cursor stdout always includes `code` (stable machine-readable decision code)
|
|
893
911
|
* so agents can distinguish policy denials from host-integration failures
|
|
@@ -896,9 +914,45 @@ export function decideHook(input, seams = {}) {
|
|
|
896
914
|
*/
|
|
897
915
|
export function renderHostDecision(host, decision) {
|
|
898
916
|
if (decision.verdict === "allow") {
|
|
917
|
+
const soft = softAgentsRebindWireText(decision);
|
|
899
918
|
if (host === "cursor") {
|
|
919
|
+
if (decision.event === "session.start" && soft !== null) {
|
|
920
|
+
// Cursor sessionStart injects additional_context into the conversation.
|
|
921
|
+
return JSON.stringify({
|
|
922
|
+
permission: "allow",
|
|
923
|
+
code: decision.code,
|
|
924
|
+
additional_context: soft,
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
if (decision.event === "session.compact" && soft !== null) {
|
|
928
|
+
// Cursor preCompact is observational; user_message surfaces the soft cue.
|
|
929
|
+
// Hard re-arm still ran via bookkeeping; agent path also gets SessionStart on resume.
|
|
930
|
+
return JSON.stringify({
|
|
931
|
+
user_message: soft,
|
|
932
|
+
code: decision.code,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
900
935
|
return JSON.stringify({ permission: "allow", code: decision.code });
|
|
901
936
|
}
|
|
937
|
+
if (soft !== null &&
|
|
938
|
+
(decision.event === "session.start" || decision.event === "session.compact")) {
|
|
939
|
+
if (host === "claude" || host === "codex") {
|
|
940
|
+
const hookEventName = decision.event === "session.start" ? "SessionStart" : "PostCompact";
|
|
941
|
+
return JSON.stringify({
|
|
942
|
+
hookSpecificOutput: {
|
|
943
|
+
hookEventName,
|
|
944
|
+
additionalContext: soft,
|
|
945
|
+
},
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
if (host === "grok") {
|
|
949
|
+
return JSON.stringify({
|
|
950
|
+
decision: "allow",
|
|
951
|
+
reason: soft,
|
|
952
|
+
additional_context: soft,
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
}
|
|
902
956
|
return "";
|
|
903
957
|
}
|
|
904
958
|
switch (host) {
|
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,9 @@ export * as branch from "./branch/index.js";
|
|
|
14
14
|
export * as cache from "./cache/index.js";
|
|
15
15
|
export * as capacity from "./capacity/index.js";
|
|
16
16
|
export * as codebase from "./codebase/index.js";
|
|
17
|
+
export * as consumerCheckContract from "./consumer-check-contract/index.js";
|
|
17
18
|
export * from "./coverage-hotspots/index.js";
|
|
19
|
+
export * as deliveryAttempt from "./delivery-attempt/index.js";
|
|
18
20
|
export * as doctor from "./doctor/index.js";
|
|
19
21
|
export * from "./encoding/index.js";
|
|
20
22
|
export * as escalation from "./escalation/index.js";
|
|
@@ -35,6 +37,7 @@ export * as lifecycle from "./lifecycle/index.js";
|
|
|
35
37
|
export * as metrics from "./metrics/index.js";
|
|
36
38
|
export * as orchestration from "./orchestration/index.js";
|
|
37
39
|
export * as packs from "./packs/index.js";
|
|
40
|
+
export * as parentTurnShape from "./parent-turn-shape/index.js";
|
|
38
41
|
export * as platform from "./platform/index.js";
|
|
39
42
|
export * as policy from "./policy/index.js";
|
|
40
43
|
export * as prClosingKeywords from "./pr-closing-keywords/index.js";
|
|
@@ -51,11 +54,13 @@ export * as render from "./render/index.js";
|
|
|
51
54
|
export * as resolution from "./resolution/index.js";
|
|
52
55
|
export * as scm from "./scm/index.js";
|
|
53
56
|
export * as scope from "./scope/index.js";
|
|
57
|
+
export * as scopeProvenance from "./scope-provenance/index.js";
|
|
54
58
|
export * as session from "./session/index.js";
|
|
55
59
|
export * as slash from "./slash/index.js";
|
|
56
60
|
export * as slice from "./slice/index.js";
|
|
57
61
|
export * as storyReady from "./story-ready/index.js";
|
|
58
62
|
export * as swarm from "./swarm/index.js";
|
|
63
|
+
export * as testBoundary from "./test-boundary/index.js";
|
|
59
64
|
export * as toolEvents from "./tool-events/index.js";
|
|
60
65
|
export * as triage from "./triage/index.js";
|
|
61
66
|
export * as userConfig from "./user-config/index.js";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,9 @@ export * as branch from "./branch/index.js";
|
|
|
14
14
|
export * as cache from "./cache/index.js";
|
|
15
15
|
export * as capacity from "./capacity/index.js";
|
|
16
16
|
export * as codebase from "./codebase/index.js";
|
|
17
|
+
export * as consumerCheckContract from "./consumer-check-contract/index.js";
|
|
17
18
|
export * from "./coverage-hotspots/index.js";
|
|
19
|
+
export * as deliveryAttempt from "./delivery-attempt/index.js";
|
|
18
20
|
export * as doctor from "./doctor/index.js";
|
|
19
21
|
export * from "./encoding/index.js";
|
|
20
22
|
export * as escalation from "./escalation/index.js";
|
|
@@ -36,6 +38,7 @@ export * as lifecycle from "./lifecycle/index.js";
|
|
|
36
38
|
export * as metrics from "./metrics/index.js";
|
|
37
39
|
export * as orchestration from "./orchestration/index.js";
|
|
38
40
|
export * as packs from "./packs/index.js";
|
|
41
|
+
export * as parentTurnShape from "./parent-turn-shape/index.js";
|
|
39
42
|
export * as platform from "./platform/index.js";
|
|
40
43
|
export * as policy from "./policy/index.js";
|
|
41
44
|
export * as prClosingKeywords from "./pr-closing-keywords/index.js";
|
|
@@ -52,11 +55,13 @@ export * as render from "./render/index.js";
|
|
|
52
55
|
export * as resolution from "./resolution/index.js";
|
|
53
56
|
export * as scm from "./scm/index.js";
|
|
54
57
|
export * as scope from "./scope/index.js";
|
|
58
|
+
export * as scopeProvenance from "./scope-provenance/index.js";
|
|
55
59
|
export * as session from "./session/index.js";
|
|
56
60
|
export * as slash from "./slash/index.js";
|
|
57
61
|
export * as slice from "./slice/index.js";
|
|
58
62
|
export * as storyReady from "./story-ready/index.js";
|
|
59
63
|
export * as swarm from "./swarm/index.js";
|
|
64
|
+
export * as testBoundary from "./test-boundary/index.js";
|
|
60
65
|
export * as toolEvents from "./tool-events/index.js";
|
|
61
66
|
export * as triage from "./triage/index.js";
|
|
62
67
|
export * as userConfig from "./user-config/index.js";
|
|
@@ -37,6 +37,11 @@ export const CANONICAL_GITIGNORE_BASELINE = [
|
|
|
37
37
|
"vbrief/.triage-cache/scope-lifecycle.jsonl",
|
|
38
38
|
"vbrief/.triage-cache/decompositions/",
|
|
39
39
|
"vbrief/.triage-cache/doctor-state.json",
|
|
40
|
+
// Per-clone session state (#3146): throttle / release-availability JSON
|
|
41
|
+
// written under the lifecycle .triage-cache; selective ignore only (hybrid
|
|
42
|
+
// #1144 — never blanket-ignore the whole .triage-cache directory).
|
|
43
|
+
"vbrief/.triage-cache/staleness-tickler-state.json",
|
|
44
|
+
"vbrief/.triage-cache/release-availability-state.json",
|
|
40
45
|
// Symmetric `xbrief/` layout entries (#2348). On the migrated `xbrief/` tree
|
|
41
46
|
// the engine writes operator-private triage-cache files to
|
|
42
47
|
// `xbrief/.triage-cache/`; without these the paths are trackable, violating
|
|
@@ -47,6 +52,8 @@ export const CANONICAL_GITIGNORE_BASELINE = [
|
|
|
47
52
|
"xbrief/.triage-cache/scope-lifecycle.jsonl",
|
|
48
53
|
"xbrief/.triage-cache/decompositions/",
|
|
49
54
|
"xbrief/.triage-cache/doctor-state.json",
|
|
55
|
+
"xbrief/.triage-cache/staleness-tickler-state.json",
|
|
56
|
+
"xbrief/.triage-cache/release-availability-state.json",
|
|
50
57
|
"vbrief/*.lock",
|
|
51
58
|
".deft/core.bak-*/",
|
|
52
59
|
".deft/*.bak-*",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* and scope briefs are never installer-managed; if they reappear in
|
|
9
9
|
* `installerManagedMatchers()`, unit tests and deposit-time assert fail closed.
|
|
10
10
|
*
|
|
11
|
-
* Refs #1576, #1453, #1430, #3029, #3030.
|
|
11
|
+
* Refs #1576, #1453, #1430, #3029, #3030, #3127, #3117.
|
|
12
12
|
*/
|
|
13
13
|
import { type InitDepositIo } from "./constants.js";
|
|
14
14
|
export declare const CODEQL_CONFIG_REL = ".github/codeql/codeql-config.yml";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* and scope briefs are never installer-managed; if they reappear in
|
|
9
9
|
* `installerManagedMatchers()`, unit tests and deposit-time assert fail closed.
|
|
10
10
|
*
|
|
11
|
-
* Refs #1576, #1453, #1430, #3029, #3030.
|
|
11
|
+
* Refs #1576, #1453, #1430, #3029, #3030, #3127, #3117.
|
|
12
12
|
*/
|
|
13
13
|
import { execFileSync } from "node:child_process";
|
|
14
14
|
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
@@ -82,6 +82,16 @@ export function installerManagedMatchers() {
|
|
|
82
82
|
{ exact: CODEQL_CONFIG_REL },
|
|
83
83
|
{ exact: CORE_GUARD_WORKFLOW_REL },
|
|
84
84
|
{ exact: "Taskfile.yml" },
|
|
85
|
+
// Upgrade co-travel unit (#3127): npm pin + lock follow-through and the
|
|
86
|
+
// freshness live stamp (#3117) are part of a normal framework upgrade, not
|
|
87
|
+
// product feature work. Path-level allow for v1 (stricter “only
|
|
88
|
+
// @deftai/directive version changed” is a follow-on if needed).
|
|
89
|
+
// Mixing .deft/core/** with true app/product paths still fails (#1430).
|
|
90
|
+
{ exact: "package.json" },
|
|
91
|
+
{ exact: "package-lock.json" },
|
|
92
|
+
{ exact: "pnpm-lock.yaml" },
|
|
93
|
+
{ exact: "yarn.lock" },
|
|
94
|
+
{ exact: ".deft/GENERATION.json" },
|
|
85
95
|
// Legacy vbrief/ tree -- retained for not-yet-migrated consumers.
|
|
86
96
|
{ exact: "vbrief/.deft-version" },
|
|
87
97
|
{ exact: "vbrief/vbrief.md" },
|
|
@@ -476,9 +486,11 @@ export function printCommitGuidance(io, paths, staged) {
|
|
|
476
486
|
if (paths.length === 0)
|
|
477
487
|
return;
|
|
478
488
|
const addCmd = `git add ${paths.join(" ")}`;
|
|
479
|
-
io.printf("\nCommit hygiene (#1453, #1671): keep the framework
|
|
480
|
-
io.printf("Do NOT use `git add -A` -- mixing the payload with
|
|
489
|
+
io.printf("\nCommit hygiene (#1453, #1671, #3127): keep the framework upgrade in its OWN branch/PR.\n");
|
|
490
|
+
io.printf("Do NOT use `git add -A` -- mixing the payload with product/app files trips the\n");
|
|
481
491
|
io.printf("deft-core-guard CI check.\n");
|
|
492
|
+
io.printf("One upgrade PR MAY co-travel: .deft/core/** + installer-managed deposits + package.json\n");
|
|
493
|
+
io.printf("pin/lock + .deft/GENERATION.json. True app/product paths still require a separate PR.\n");
|
|
482
494
|
if (staged) {
|
|
483
495
|
io.printf("The installer already staged ONLY these framework + installer-managed paths:\n");
|
|
484
496
|
io.printf(` ${addCmd}\n`);
|
|
@@ -488,7 +500,7 @@ export function printCommitGuidance(io, paths, staged) {
|
|
|
488
500
|
io.printf(` ${addCmd}\n`);
|
|
489
501
|
}
|
|
490
502
|
io.printf("Then take the framework deposit through the full PR lifecycle so deft-core-guard\n");
|
|
491
|
-
io.printf("evaluates a clean, standalone PR:\n");
|
|
503
|
+
io.printf("evaluates a clean, standalone upgrade PR:\n");
|
|
492
504
|
io.printf(` 1. Branch: git switch -c ${COMMIT_HYGIENE_BRANCH_NAME}\n`);
|
|
493
505
|
io.printf(' 2. Commit: git commit -m "chore(deft): update framework payload"\n');
|
|
494
506
|
io.printf(` 3. Push: git push -u origin ${COMMIT_HYGIENE_BRANCH_NAME}\n`);
|
|
@@ -15,6 +15,7 @@ import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
|
15
15
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
16
16
|
import { stampLiveGeneration } from "../freshness/generation.js";
|
|
17
17
|
import { renderProjectDefinition } from "../render/project-render.js";
|
|
18
|
+
import { depositOpenClawSoftRebindSkill } from "../session/openclaw-soft-rebind-deposit.js";
|
|
18
19
|
import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
|
|
19
20
|
import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
|
|
20
21
|
import { removeStaleMigratedFrameworkNarrative } from "../xbrief-migrate/migrate-project.js";
|
|
@@ -201,6 +202,10 @@ export async function runInitDeposit(args, io, seams = {}) {
|
|
|
201
202
|
writeConsumerGitHooks(projectDir, deftDir, io, seams.gitHooks);
|
|
202
203
|
writeAgentHookDeposit(projectDir, io);
|
|
203
204
|
writeSlashCommandDeposit(projectDir, io);
|
|
205
|
+
// #3171: OpenClaw soft AGENTS re-bind skill when OC signals present (fail-closed otherwise).
|
|
206
|
+
depositOpenClawSoftRebindSkill({
|
|
207
|
+
/* env defaults; skip when OpenClaw not detected */
|
|
208
|
+
});
|
|
204
209
|
// #3064: OpenClaw L2 product-command skills when OC signals present (fail-closed otherwise).
|
|
205
210
|
depositOpenClawL2ProductCommands({
|
|
206
211
|
projectRoot: projectDir,
|
|
@@ -22,6 +22,7 @@ import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
|
22
22
|
import { detectNoDeftDirective, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, NO_DEFT_DIRECTIVE_INCONSISTENT_POLICY, } from "../policy/no-deft-directive.js";
|
|
23
23
|
import { runOrgForceOnMigration } from "../policy/org-force-on-migration.js";
|
|
24
24
|
import { checkLocalEngineIntegrity, classify, ENGINE_PACKAGE, plan, renderGlobalInstall, resolveEngine, } from "../resolution/index.js";
|
|
25
|
+
import { depositOpenClawSoftRebindSkill } from "../session/openclaw-soft-rebind-deposit.js";
|
|
25
26
|
import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
|
|
26
27
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
27
28
|
import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
|
|
@@ -506,6 +507,8 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
506
507
|
// #75 residual: multi-host thin skill discovery (mirror `.agents/skills` inventory).
|
|
507
508
|
writeMultiHostSkillDiscovery(projectDir, io);
|
|
508
509
|
writeSlashCommandDeposit(projectDir, io);
|
|
510
|
+
// #3171: OpenClaw soft AGENTS re-bind skill when OC signals present (fail-closed otherwise).
|
|
511
|
+
depositOpenClawSoftRebindSkill({});
|
|
509
512
|
// #3064: OpenClaw L2 product-command skills when OC signals present (fail-closed otherwise).
|
|
510
513
|
depositOpenClawL2ProductCommands({
|
|
511
514
|
projectRoot: projectDir,
|
|
@@ -507,11 +507,12 @@ function coreGuardWorkflowContent() {
|
|
|
507
507
|
const baseSha = githubActionsExpr("github.event.pull_request.base.sha");
|
|
508
508
|
const headSha = githubActionsExpr("github.event.pull_request.head.sha");
|
|
509
509
|
return ("name: deft-core-guard\n\n" +
|
|
510
|
-
"# Deft framework guard (#1430): a single PR should not mix changes to the\n" +
|
|
511
|
-
"# vendored framework payload (.deft/core/**) with
|
|
512
|
-
"#
|
|
513
|
-
"#
|
|
514
|
-
"#
|
|
510
|
+
"# Deft framework guard (#1430 / #3127): a single PR should not mix changes to the\n" +
|
|
511
|
+
"# vendored framework payload (.deft/core/**) with true application/product files.\n" +
|
|
512
|
+
"# One upgrade PR MAY include deposit + installer-managed paths + package.json pin/lock\n" +
|
|
513
|
+
"# + .deft/GENERATION.json. Framework updates come from `deft update` / install and should\n" +
|
|
514
|
+
"# land without product feature work so reviewers treat the payload as machine-managed.\n" +
|
|
515
|
+
"# Delete this file if you do not want the guard.\n" +
|
|
515
516
|
"on:\n" +
|
|
516
517
|
" pull_request:\n\n" +
|
|
517
518
|
"permissions:\n" +
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent turn-shape hard-stop for text-repetition hang (FC14 / #3131).
|
|
3
|
+
*
|
|
4
|
+
* Soft skill prose (#2943) alone is insufficient: parents still burn the full
|
|
5
|
+
* output budget on identical progress sentences with zero tool_use after leaf
|
|
6
|
+
* announce. This pure library is the Directive-side machine-checkable gate.
|
|
7
|
+
*
|
|
8
|
+
* Hosts (OpenClaw, swarm parents, review monitors) SHOULD evaluate the parent
|
|
9
|
+
* turn stream mid-generation and hard-stop when `ok` is false.
|
|
10
|
+
*
|
|
11
|
+
* Illegal shape (MUST NOT):
|
|
12
|
+
* N > maxIdenticalWithoutTool (default 2) near-identical assistant sentences
|
|
13
|
+
* (or streaming text chunks) in one turn with no tool_use and no yield.
|
|
14
|
+
*
|
|
15
|
+
* Legal post-announce shapes:
|
|
16
|
+
* 1. Tool batch (ground-truth) then one consolidate
|
|
17
|
+
* 2. sessions_yield / wait without filler
|
|
18
|
+
* 3. One short user answer that is NOT a repeated progress line
|
|
19
|
+
*/
|
|
20
|
+
/** Failure class codes for operator / host recovery surfaces. */
|
|
21
|
+
export type ParentTurnFailClass = "none" | "FC14" | "text-repetition-hang" | "progress-only-no-tool";
|
|
22
|
+
/** Ordered events observed within a single parent turn. */
|
|
23
|
+
export type ParentTurnEvent = {
|
|
24
|
+
kind: "assistant_text";
|
|
25
|
+
text: string;
|
|
26
|
+
} | {
|
|
27
|
+
kind: "tool_use";
|
|
28
|
+
name?: string;
|
|
29
|
+
} | {
|
|
30
|
+
kind: "yield";
|
|
31
|
+
};
|
|
32
|
+
export interface ParentTurnShapeInput {
|
|
33
|
+
/** Ordered events from one parent turn (streaming deltas may be separate). */
|
|
34
|
+
readonly events: readonly ParentTurnEvent[];
|
|
35
|
+
/**
|
|
36
|
+
* When true, multi-sentence progress-only text with zero tools/yield is also
|
|
37
|
+
* illegal even without exact N>2 identity (post-`subagent_announce` policy).
|
|
38
|
+
*/
|
|
39
|
+
readonly afterSubagentAnnounce?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Max near-identical consecutive (or within-turn) text units allowed without
|
|
42
|
+
* tool_use / yield. Issue contract: MUST NOT emit N>2 → default max = 2.
|
|
43
|
+
*/
|
|
44
|
+
readonly maxIdenticalWithoutTool?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ParentTurnShapeResult {
|
|
47
|
+
readonly ok: boolean;
|
|
48
|
+
/** Primary fail class (`FC14` aliases `text-repetition-hang`). */
|
|
49
|
+
readonly failClass: ParentTurnFailClass;
|
|
50
|
+
readonly reasons: readonly string[];
|
|
51
|
+
/** Highest count of near-identical text units observed without tool/yield. */
|
|
52
|
+
readonly maxIdenticalCount: number;
|
|
53
|
+
readonly hasToolUse: boolean;
|
|
54
|
+
readonly hasYield: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Canonical fail class for the hang (#3131 / soft FC14 recurrence of #2943). */
|
|
57
|
+
export declare const PARENT_TURN_FAIL_FC14: "FC14";
|
|
58
|
+
/** Default max identical text units without tool/yield (N>2 is illegal). */
|
|
59
|
+
export declare const DEFAULT_MAX_IDENTICAL_WITHOUT_TOOL = 2;
|
|
60
|
+
/**
|
|
61
|
+
* Normalize assistant text for identity comparison.
|
|
62
|
+
* Collapses whitespace, lowercases, strips common trailing punctuation.
|
|
63
|
+
*/
|
|
64
|
+
export declare function normalizeTurnText(text: string): string;
|
|
65
|
+
/** Split a blob into sentence-like units (periods, newlines, ellipsis). */
|
|
66
|
+
export declare function splitTextUnits(text: string): string[];
|
|
67
|
+
/**
|
|
68
|
+
* True when two normalized strings are near-identical (exact, containment with
|
|
69
|
+
* high length ratio, or high word Jaccard). Short units never match.
|
|
70
|
+
*/
|
|
71
|
+
export declare function isNearIdentical(a: string, b: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Detect when a single blob itself embeds the same sentence many times
|
|
74
|
+
* (model streams one giant assistant message of repeated lines).
|
|
75
|
+
*/
|
|
76
|
+
export declare function countRepeatedUnitsInBlob(text: string): number;
|
|
77
|
+
/**
|
|
78
|
+
* Evaluate whether a parent turn shape is legal under the FC14 hard-stop.
|
|
79
|
+
* Pure / side-effect free — safe for mid-stream host gates and unit tests.
|
|
80
|
+
*/
|
|
81
|
+
export declare function evaluateParentTurnShape(input: ParentTurnShapeInput): ParentTurnShapeResult;
|
|
82
|
+
/** Convenience: true when the turn is an illegal text-repetition hang. */
|
|
83
|
+
export declare function isTextRepetitionHang(input: ParentTurnShapeInput): boolean;
|
|
84
|
+
//# sourceMappingURL=evaluate.d.ts.map
|