@kernel.chat/kbot 3.99.21 → 3.99.22
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/critic-gate.d.ts
CHANGED
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
* ~/.kbot/config.json: critic_enabled (bool), critic_strictness (0..1).
|
|
6
6
|
* Hard disable: env KBOT_NO_CRITIC=1.
|
|
7
7
|
*/
|
|
8
|
+
import { type RFClass } from './critic-taxonomy.js';
|
|
8
9
|
export interface CriticVerdict {
|
|
9
10
|
accept: boolean;
|
|
10
11
|
reason?: string;
|
|
11
12
|
retry_hint?: string;
|
|
12
13
|
confidence: number;
|
|
14
|
+
/** RF taxonomy class when a rule-based classifier fired (arXiv:2601.22208). */
|
|
15
|
+
failure_class?: RFClass;
|
|
13
16
|
}
|
|
14
17
|
export interface GateOpts {
|
|
15
18
|
strictness?: number;
|
package/dist/critic-gate.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Hard disable: env KBOT_NO_CRITIC=1.
|
|
7
7
|
*/
|
|
8
8
|
import { loadConfig } from './auth.js';
|
|
9
|
+
import { classifyToolResult } from './critic-taxonomy.js';
|
|
9
10
|
const TRUSTED_TOOLS = new Set([
|
|
10
11
|
'read', 'read_file', 'kbot_read', 'kbot_read_file',
|
|
11
12
|
'glob', 'kbot_glob', 'grep', 'kbot_grep', 'list_directory', 'ls',
|
|
@@ -166,13 +167,15 @@ export async function gateToolResult(tool, args, result, opts = {}) {
|
|
|
166
167
|
if (isTriviallyValid(tool, resultText)) {
|
|
167
168
|
return { accept: true, confidence: 0.9, reason: 'trivial-valid fast path' };
|
|
168
169
|
}
|
|
169
|
-
//
|
|
170
|
-
|
|
170
|
+
// Rule-based RF classifier — cheap, no LLM. High-confidence hits short-circuit.
|
|
171
|
+
const rf = classifyToolResult(resultText);
|
|
172
|
+
if (rf && rf.confidence >= 0.8) {
|
|
171
173
|
return {
|
|
172
174
|
accept: false,
|
|
173
|
-
confidence:
|
|
174
|
-
reason:
|
|
175
|
-
retry_hint: '
|
|
175
|
+
confidence: rf.confidence,
|
|
176
|
+
reason: `${rf.class}: ${rf.evidence}`,
|
|
177
|
+
retry_hint: 'Taxonomy match — try different arguments or a different tool.',
|
|
178
|
+
failure_class: rf.class,
|
|
176
179
|
};
|
|
177
180
|
}
|
|
178
181
|
const userPrompt = buildUserPrompt(tool, args, resultText);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning-failure taxonomy for the critic gate.
|
|
3
|
+
*
|
|
4
|
+
* Adopted verbatim from "Stalled, Biased, and Confused: Uncovering Reasoning
|
|
5
|
+
* Failures in LLMs for Cloud-Based Root Cause Analysis" (arXiv:2601.22208,
|
|
6
|
+
* 2026). 16 modes, evaluated across 48k simulated scenarios on ReAct and
|
|
7
|
+
* Plan-and-Execute workflows. The domain is cloud RCA but the modes generalize
|
|
8
|
+
* to tool-using agents.
|
|
9
|
+
*
|
|
10
|
+
* Purpose here: replace ad-hoc ERROR_KEYWORDS matching in critic-gate.ts with
|
|
11
|
+
* a typed classifier so that when the critic rejects a tool result we can
|
|
12
|
+
* attribute the rejection to a named class. Makes FP-rate measurement tractable
|
|
13
|
+
* per-class instead of in aggregate.
|
|
14
|
+
*
|
|
15
|
+
* This module is pure (no I/O, no LLM calls). Classification is rule-based and
|
|
16
|
+
* intentionally conservative — when nothing matches, returns null and the
|
|
17
|
+
* existing critic fallback handles it.
|
|
18
|
+
*/
|
|
19
|
+
export type RFClass = 'RF-01-fabricated-evidence' | 'RF-02-metric-interpretation' | 'RF-03-confused-provenance' | 'RF-04-temporal-misordering' | 'RF-05-spurious-causal-attribution' | 'RF-06-unjustified-instance-specificity' | 'RF-07-arbitrary-evidence-selection' | 'RF-08-evidential-insufficiency' | 'RF-09-failure-to-update-belief' | 'RF-10-simulation-role-confusion' | 'RF-11-excessive-speculation' | 'RF-12-repetition-failure-to-resume' | 'RF-13-anchoring-bias' | 'RF-14-invalid-inference-pattern' | 'RF-15-internal-contradiction' | 'RF-16-arithmetic-error';
|
|
20
|
+
export interface RFClassification {
|
|
21
|
+
class: RFClass;
|
|
22
|
+
evidence: string;
|
|
23
|
+
confidence: number;
|
|
24
|
+
}
|
|
25
|
+
export interface TrajectoryStep {
|
|
26
|
+
tool: string;
|
|
27
|
+
args: Record<string, unknown>;
|
|
28
|
+
result: string;
|
|
29
|
+
timestampMs: number;
|
|
30
|
+
}
|
|
31
|
+
/** RF-12: trajectory-level — last N steps repeat the same tool+args. */
|
|
32
|
+
export declare function detectRepetition(trajectory: TrajectoryStep[], windowSize?: number): RFClassification | null;
|
|
33
|
+
/**
|
|
34
|
+
* Classify a single tool result against the RF taxonomy.
|
|
35
|
+
*
|
|
36
|
+
* Returns the highest-confidence match, or null if nothing fires. Callers
|
|
37
|
+
* should treat null as "no taxonomy signal" — not as "result is fine".
|
|
38
|
+
*/
|
|
39
|
+
export declare function classifyToolResult(result: string): RFClassification | null;
|
|
40
|
+
//# sourceMappingURL=critic-taxonomy.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning-failure taxonomy for the critic gate.
|
|
3
|
+
*
|
|
4
|
+
* Adopted verbatim from "Stalled, Biased, and Confused: Uncovering Reasoning
|
|
5
|
+
* Failures in LLMs for Cloud-Based Root Cause Analysis" (arXiv:2601.22208,
|
|
6
|
+
* 2026). 16 modes, evaluated across 48k simulated scenarios on ReAct and
|
|
7
|
+
* Plan-and-Execute workflows. The domain is cloud RCA but the modes generalize
|
|
8
|
+
* to tool-using agents.
|
|
9
|
+
*
|
|
10
|
+
* Purpose here: replace ad-hoc ERROR_KEYWORDS matching in critic-gate.ts with
|
|
11
|
+
* a typed classifier so that when the critic rejects a tool result we can
|
|
12
|
+
* attribute the rejection to a named class. Makes FP-rate measurement tractable
|
|
13
|
+
* per-class instead of in aggregate.
|
|
14
|
+
*
|
|
15
|
+
* This module is pure (no I/O, no LLM calls). Classification is rule-based and
|
|
16
|
+
* intentionally conservative — when nothing matches, returns null and the
|
|
17
|
+
* existing critic fallback handles it.
|
|
18
|
+
*/
|
|
19
|
+
const SPECULATION_MARKERS = [
|
|
20
|
+
'i think', 'probably', 'might be', 'could be', 'perhaps',
|
|
21
|
+
'it seems', 'i believe', 'likely', "i'm guessing", 'my guess',
|
|
22
|
+
];
|
|
23
|
+
const FABRICATION_MARKERS = [
|
|
24
|
+
'as an ai', 'i cannot actually', 'i don\'t have access', 'hypothetically',
|
|
25
|
+
'let\'s assume', 'for the sake of', 'imagine that',
|
|
26
|
+
];
|
|
27
|
+
const UNRESOLVED_ERROR_MARKERS = [
|
|
28
|
+
'enoent', 'permission denied', 'eacces', 'connection refused',
|
|
29
|
+
'timeout', 'econnrefused', 'not found',
|
|
30
|
+
];
|
|
31
|
+
function lower(s) {
|
|
32
|
+
return (s || '').toLowerCase();
|
|
33
|
+
}
|
|
34
|
+
/** RF-01: tool result contains hedging language presented as fact. */
|
|
35
|
+
function detectFabrication(text) {
|
|
36
|
+
const lc = lower(text);
|
|
37
|
+
for (const m of FABRICATION_MARKERS) {
|
|
38
|
+
if (lc.includes(m)) {
|
|
39
|
+
return {
|
|
40
|
+
class: 'RF-01-fabricated-evidence',
|
|
41
|
+
evidence: `hedging marker "${m}" in tool result`,
|
|
42
|
+
confidence: 0.7,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
/** RF-08: result is structurally empty or shorter than task demands. */
|
|
49
|
+
function detectEvidentialInsufficiency(text) {
|
|
50
|
+
const trimmed = (text || '').trim();
|
|
51
|
+
if (trimmed.length === 0) {
|
|
52
|
+
return {
|
|
53
|
+
class: 'RF-08-evidential-insufficiency',
|
|
54
|
+
evidence: 'empty tool result',
|
|
55
|
+
confidence: 0.95,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.length < 16 && !/\d/.test(trimmed)) {
|
|
59
|
+
return {
|
|
60
|
+
class: 'RF-08-evidential-insufficiency',
|
|
61
|
+
evidence: `result is ${trimmed.length} chars with no numeric content`,
|
|
62
|
+
confidence: 0.55,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
/** RF-11: speculation language in what should be a factual tool result. */
|
|
68
|
+
function detectExcessiveSpeculation(text) {
|
|
69
|
+
const lc = lower(text);
|
|
70
|
+
const hits = SPECULATION_MARKERS.filter(m => lc.includes(m));
|
|
71
|
+
if (hits.length >= 2) {
|
|
72
|
+
return {
|
|
73
|
+
class: 'RF-11-excessive-speculation',
|
|
74
|
+
evidence: `speculation markers: ${hits.slice(0, 3).join(', ')}`,
|
|
75
|
+
confidence: 0.6,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
/** RF-10: model output claims tool ran when the result text shows it did not. */
|
|
81
|
+
function detectSimulationConfusion(text) {
|
|
82
|
+
const lc = lower(text);
|
|
83
|
+
const hasUnresolvedError = UNRESOLVED_ERROR_MARKERS.some(m => lc.includes(m));
|
|
84
|
+
const claimsSuccess = /\b(successfully|completed|done|finished)\b/.test(lc);
|
|
85
|
+
if (hasUnresolvedError && claimsSuccess) {
|
|
86
|
+
return {
|
|
87
|
+
class: 'RF-10-simulation-role-confusion',
|
|
88
|
+
evidence: 'result claims success and contains an error marker',
|
|
89
|
+
confidence: 0.85,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/** RF-15: internal contradiction — two opposing claims in one result. */
|
|
95
|
+
function detectInternalContradiction(text) {
|
|
96
|
+
const lc = lower(text);
|
|
97
|
+
if (/\b(is|was|are)\s+\w+/.test(lc) && /\bis\s+not\b.*\bis\b/.test(lc)) {
|
|
98
|
+
return {
|
|
99
|
+
class: 'RF-15-internal-contradiction',
|
|
100
|
+
evidence: 'opposing "is"/"is not" claims within result',
|
|
101
|
+
confidence: 0.5,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/** RF-12: trajectory-level — last N steps repeat the same tool+args. */
|
|
107
|
+
export function detectRepetition(trajectory, windowSize = 3) {
|
|
108
|
+
if (trajectory.length < windowSize)
|
|
109
|
+
return null;
|
|
110
|
+
const window = trajectory.slice(-windowSize);
|
|
111
|
+
const first = window[0];
|
|
112
|
+
const key = `${first.tool}:${JSON.stringify(first.args)}`;
|
|
113
|
+
const allSame = window.every(s => `${s.tool}:${JSON.stringify(s.args)}` === key);
|
|
114
|
+
if (allSame) {
|
|
115
|
+
return {
|
|
116
|
+
class: 'RF-12-repetition-failure-to-resume',
|
|
117
|
+
evidence: `${windowSize} consecutive identical calls to ${first.tool}`,
|
|
118
|
+
confidence: 0.9,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Classify a single tool result against the RF taxonomy.
|
|
125
|
+
*
|
|
126
|
+
* Returns the highest-confidence match, or null if nothing fires. Callers
|
|
127
|
+
* should treat null as "no taxonomy signal" — not as "result is fine".
|
|
128
|
+
*/
|
|
129
|
+
export function classifyToolResult(result) {
|
|
130
|
+
const detectors = [
|
|
131
|
+
detectSimulationConfusion,
|
|
132
|
+
detectFabrication,
|
|
133
|
+
detectEvidentialInsufficiency,
|
|
134
|
+
detectExcessiveSpeculation,
|
|
135
|
+
detectInternalContradiction,
|
|
136
|
+
];
|
|
137
|
+
let best = null;
|
|
138
|
+
for (const d of detectors) {
|
|
139
|
+
const hit = d(result);
|
|
140
|
+
if (hit && (!best || hit.confidence > best.confidence)) {
|
|
141
|
+
best = hit;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return best;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=critic-taxonomy.js.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-Decoupled Planning (TDP) — DAG node types.
|
|
3
|
+
*
|
|
4
|
+
* Adopted from "Beyond Entangled Planning: Task-Decoupled Planning for
|
|
5
|
+
* Long-Horizon Agents" (arXiv:2601.07577, 2026). The paper reports up to 82%
|
|
6
|
+
* token reduction by decomposing a task into a DAG of sub-goals, each executed
|
|
7
|
+
* under a context scoped to only that node's ancestors — not the full history.
|
|
8
|
+
*
|
|
9
|
+
* This module is types-only. Phase 2 of the hierarchical planner will wrap
|
|
10
|
+
* existing Phase nodes as DAG nodes; no runtime here.
|
|
11
|
+
*
|
|
12
|
+
* Scoping rule (paper §3.2): a DAG node's context window contains
|
|
13
|
+
* - the node's own sub-goal + acceptance criteria
|
|
14
|
+
* - outputs (summaries) of ancestor nodes only
|
|
15
|
+
* - the typed tool schemas available for that node
|
|
16
|
+
* Non-ancestor siblings are excluded. Replanning fires only when the node's
|
|
17
|
+
* verifier rejects — never regenerates the whole trajectory.
|
|
18
|
+
*/
|
|
19
|
+
import type { Phase, Action, TierVerdict } from './types.js';
|
|
20
|
+
/** Stable node identifier within a DAG. */
|
|
21
|
+
export type NodeId = string;
|
|
22
|
+
/**
|
|
23
|
+
* One node in the task DAG. In the kbot mapping, a `DAGNode` wraps a `Phase`
|
|
24
|
+
* and its child `Action`s, adding explicit parent edges and a scoped-context
|
|
25
|
+
* marker. Keeping `phase` as an embedded field means Phase 2 can migrate
|
|
26
|
+
* incrementally — tools that walk Phases still work.
|
|
27
|
+
*/
|
|
28
|
+
export interface DAGNode {
|
|
29
|
+
id: NodeId;
|
|
30
|
+
/** Parent node ids — the node's inputs. Empty for root. */
|
|
31
|
+
parents: NodeId[];
|
|
32
|
+
/** The wrapped Phase. Its `id` is duplicated here as the node id. */
|
|
33
|
+
phase: Phase;
|
|
34
|
+
/** Actions executed inside this node. */
|
|
35
|
+
actions: Action[];
|
|
36
|
+
/**
|
|
37
|
+
* Summary of the node's output, produced once status flips to done.
|
|
38
|
+
* This is what descendants see — not the full action trace.
|
|
39
|
+
*/
|
|
40
|
+
outputSummary?: string;
|
|
41
|
+
/** Verdicts emitted against this node only. */
|
|
42
|
+
verdicts: TierVerdict[];
|
|
43
|
+
status: 'pending' | 'running' | 'done' | 'failed';
|
|
44
|
+
}
|
|
45
|
+
export interface TaskDAG {
|
|
46
|
+
/** Root node ids — usually one. */
|
|
47
|
+
roots: NodeId[];
|
|
48
|
+
nodes: Record<NodeId, DAGNode>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build the context a node sees during execution. Paper §3.2 calls this the
|
|
52
|
+
* "scoped context". Returns ancestor summaries plus the node's own sub-goal.
|
|
53
|
+
*
|
|
54
|
+
* Ordering: breadth-first from roots, so earlier context appears first in the
|
|
55
|
+
* prompt. The ancestor set is closed under the transitive parent relation.
|
|
56
|
+
*/
|
|
57
|
+
export declare function buildScopedContext(dag: TaskDAG, nodeId: NodeId): {
|
|
58
|
+
subGoal: string;
|
|
59
|
+
ancestorSummaries: Array<{
|
|
60
|
+
id: NodeId;
|
|
61
|
+
summary: string;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Topological order of `dag`. Throws on cycles — DAG invariant is caller's
|
|
66
|
+
* responsibility to maintain; this is the detector of last resort.
|
|
67
|
+
*/
|
|
68
|
+
export declare function topologicalOrder(dag: TaskDAG): NodeId[];
|
|
69
|
+
/** Nodes whose parents are all done — eligible to run next. */
|
|
70
|
+
export declare function readyNodes(dag: TaskDAG): DAGNode[];
|
|
71
|
+
//# sourceMappingURL=dag.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task-Decoupled Planning (TDP) — DAG node types.
|
|
3
|
+
*
|
|
4
|
+
* Adopted from "Beyond Entangled Planning: Task-Decoupled Planning for
|
|
5
|
+
* Long-Horizon Agents" (arXiv:2601.07577, 2026). The paper reports up to 82%
|
|
6
|
+
* token reduction by decomposing a task into a DAG of sub-goals, each executed
|
|
7
|
+
* under a context scoped to only that node's ancestors — not the full history.
|
|
8
|
+
*
|
|
9
|
+
* This module is types-only. Phase 2 of the hierarchical planner will wrap
|
|
10
|
+
* existing Phase nodes as DAG nodes; no runtime here.
|
|
11
|
+
*
|
|
12
|
+
* Scoping rule (paper §3.2): a DAG node's context window contains
|
|
13
|
+
* - the node's own sub-goal + acceptance criteria
|
|
14
|
+
* - outputs (summaries) of ancestor nodes only
|
|
15
|
+
* - the typed tool schemas available for that node
|
|
16
|
+
* Non-ancestor siblings are excluded. Replanning fires only when the node's
|
|
17
|
+
* verifier rejects — never regenerates the whole trajectory.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Build the context a node sees during execution. Paper §3.2 calls this the
|
|
21
|
+
* "scoped context". Returns ancestor summaries plus the node's own sub-goal.
|
|
22
|
+
*
|
|
23
|
+
* Ordering: breadth-first from roots, so earlier context appears first in the
|
|
24
|
+
* prompt. The ancestor set is closed under the transitive parent relation.
|
|
25
|
+
*/
|
|
26
|
+
export function buildScopedContext(dag, nodeId) {
|
|
27
|
+
const node = dag.nodes[nodeId];
|
|
28
|
+
if (!node) {
|
|
29
|
+
throw new Error(`buildScopedContext: node ${nodeId} not in DAG`);
|
|
30
|
+
}
|
|
31
|
+
const ancestors = collectAncestors(dag, nodeId);
|
|
32
|
+
const ancestorSummaries = [];
|
|
33
|
+
for (const aid of ancestors) {
|
|
34
|
+
const a = dag.nodes[aid];
|
|
35
|
+
if (a?.outputSummary) {
|
|
36
|
+
ancestorSummaries.push({ id: aid, summary: a.outputSummary });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { subGoal: node.phase.objective, ancestorSummaries };
|
|
40
|
+
}
|
|
41
|
+
/** Transitive parents of `nodeId`, breadth-first, excluding the node itself. */
|
|
42
|
+
function collectAncestors(dag, nodeId) {
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const order = [];
|
|
45
|
+
const queue = [...(dag.nodes[nodeId]?.parents ?? [])];
|
|
46
|
+
while (queue.length > 0) {
|
|
47
|
+
const next = queue.shift();
|
|
48
|
+
if (seen.has(next))
|
|
49
|
+
continue;
|
|
50
|
+
seen.add(next);
|
|
51
|
+
order.push(next);
|
|
52
|
+
const parent = dag.nodes[next];
|
|
53
|
+
if (parent)
|
|
54
|
+
queue.push(...parent.parents);
|
|
55
|
+
}
|
|
56
|
+
return order;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Topological order of `dag`. Throws on cycles — DAG invariant is caller's
|
|
60
|
+
* responsibility to maintain; this is the detector of last resort.
|
|
61
|
+
*/
|
|
62
|
+
export function topologicalOrder(dag) {
|
|
63
|
+
const inDegree = {};
|
|
64
|
+
for (const id of Object.keys(dag.nodes))
|
|
65
|
+
inDegree[id] = 0;
|
|
66
|
+
for (const node of Object.values(dag.nodes)) {
|
|
67
|
+
for (const _p of node.parents) {
|
|
68
|
+
inDegree[node.id] = (inDegree[node.id] ?? 0) + 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const ready = Object.keys(inDegree).filter(id => inDegree[id] === 0);
|
|
72
|
+
const out = [];
|
|
73
|
+
while (ready.length > 0) {
|
|
74
|
+
const id = ready.shift();
|
|
75
|
+
out.push(id);
|
|
76
|
+
for (const other of Object.values(dag.nodes)) {
|
|
77
|
+
if (other.parents.includes(id)) {
|
|
78
|
+
inDegree[other.id] -= 1;
|
|
79
|
+
if (inDegree[other.id] === 0)
|
|
80
|
+
ready.push(other.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (out.length !== Object.keys(dag.nodes).length) {
|
|
85
|
+
throw new Error('topologicalOrder: cycle detected in task DAG');
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** Nodes whose parents are all done — eligible to run next. */
|
|
90
|
+
export function readyNodes(dag) {
|
|
91
|
+
return Object.values(dag.nodes).filter(n => {
|
|
92
|
+
if (n.status !== 'pending')
|
|
93
|
+
return false;
|
|
94
|
+
return n.parents.every(pid => dag.nodes[pid]?.status === 'done');
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=dag.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernel.chat/kbot",
|
|
3
|
-
"version": "3.99.
|
|
3
|
+
"version": "3.99.22",
|
|
4
4
|
"description": "Open-source terminal AI agent. 787+ tools, 35 agents, 20 providers. Dreams, learns, watches your system. Controls your phone. Fully local, fully sovereign. MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|