@aiwayds/dsh-tui-pi 2.23.0 → 2.24.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/lib/attention-jev.d.ts +80 -0
- package/lib/attention-jev.js +161 -0
- package/lib/attention-jev.js.map +1 -0
- package/lib/attention.d.ts +122 -0
- package/lib/attention.js +220 -0
- package/lib/attention.js.map +1 -0
- package/lib/dsh-events.d.ts +7 -4
- package/lib/index.js +16 -2
- package/lib/index.js.map +1 -1
- package/lib/live-widgets.js +4 -3
- package/lib/live-widgets.js.map +1 -1
- package/lib/retention.d.ts +7 -6
- package/lib/retention.js +8 -5
- package/lib/retention.js.map +1 -1
- package/lib/session.d.ts +78 -1
- package/lib/session.js +151 -42
- package/lib/session.js.map +1 -1
- package/lib/source-kind.d.ts +2 -2
- package/lib/subagent-policy.d.ts +43 -3
- package/lib/subagent-policy.js +85 -6
- package/lib/subagent-policy.js.map +1 -1
- package/lib/subagent-status-tool.d.ts +8 -0
- package/lib/subagent-status-tool.js +16 -0
- package/lib/subagent-status-tool.js.map +1 -1
- package/lib/subagent-viewer.d.ts +6 -2
- package/lib/subagent-viewer.js +34 -10
- package/lib/subagent-viewer.js.map +1 -1
- package/lib/theme-settings.d.ts +10 -0
- package/lib/theme-settings.js +19 -0
- package/lib/theme-settings.js.map +1 -1
- package/locales/en.json +5 -2
- package/locales/ja.json +5 -2
- package/locales/ko.json +5 -2
- package/locales/zh-CN.json +5 -2
- package/package.json +2 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The jev upgrade layer for attention (ticket 04): batches every dirty
|
|
3
|
+
* live child into ONE System One call — two atomic noul questions per child
|
|
4
|
+
* (`attention_<id8>`: needs-operator-attention, `spin_<id8>`: productive vs
|
|
5
|
+
* circling) over a shared compact state table — and writes the scores back
|
|
6
|
+
* through `AttentionBoard.applyJevScores`. A high-segment `spin` verdict
|
|
7
|
+
* (≥0.75; middle values are uncertainty, never evidence) becomes the
|
|
8
|
+
* `spinning` tag the early-hard-stop ladder later consumes (ticket 05).
|
|
9
|
+
*
|
|
10
|
+
* jev-optional by construction: the source ticks silently when the setting
|
|
11
|
+
* is off, the core probe fails, no key is configured, the debounce window
|
|
12
|
+
* is open, another call is in flight, or nothing is dirty — and a failed
|
|
13
|
+
* call is a silent counter bump, never a warn and never a turned-down
|
|
14
|
+
* ranking (the heuristic layer underneath is always live).
|
|
15
|
+
*/
|
|
16
|
+
import type { AgentView } from './dsh-events.ts';
|
|
17
|
+
import type { AttentionBoard, AttentionTag } from './attention.ts';
|
|
18
|
+
/** Structural slice of @aiwayds/dsh-jev-core — type-only import, erased at runtime. */
|
|
19
|
+
interface JevCoreModule {
|
|
20
|
+
classify(options: {
|
|
21
|
+
questions: Record<string, {
|
|
22
|
+
type: 'noul';
|
|
23
|
+
criteria: Record<string, string>;
|
|
24
|
+
}>;
|
|
25
|
+
state: string;
|
|
26
|
+
env?: NodeJS.ProcessEnv;
|
|
27
|
+
fetchImpl?: typeof fetch;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
answers: Record<string, {
|
|
31
|
+
kind: 'noul';
|
|
32
|
+
value: number;
|
|
33
|
+
}>;
|
|
34
|
+
model: string;
|
|
35
|
+
usage: unknown;
|
|
36
|
+
latencyMs: number;
|
|
37
|
+
}>;
|
|
38
|
+
isConfigured(sources?: {
|
|
39
|
+
env?: NodeJS.ProcessEnv;
|
|
40
|
+
}): boolean;
|
|
41
|
+
}
|
|
42
|
+
/** Minimum gap between jev dispatches (ticket 04: global debounce 30s). */
|
|
43
|
+
export declare const ATTENTION_JEV_MIN_INTERVAL_MS = 30000;
|
|
44
|
+
/** Per-call deadline: advisory data, generous but bounded. */
|
|
45
|
+
export declare const ATTENTION_JEV_TIMEOUT_MS = 8000;
|
|
46
|
+
/** High-segment line for the spin verdict — middle values are uncertainty, not evidence. */
|
|
47
|
+
export declare const SPINNING_TAG_AT = 0.75;
|
|
48
|
+
/** The id8 collision map is first-wins by view order; uuid4 id8 collisions are astronomic. */
|
|
49
|
+
export declare function buildAttentionQuestions(id8s: readonly string[]): Record<string, {
|
|
50
|
+
type: 'noul';
|
|
51
|
+
criteria: Record<string, string>;
|
|
52
|
+
}>;
|
|
53
|
+
/** The shared state table: one compact line per child, the facts both questions judge. */
|
|
54
|
+
export declare function buildAttentionState(views: readonly AgentView[], cap: number, now: number, stallOf: (childId: string) => number): string;
|
|
55
|
+
/** Read the verdict map back onto child ids; the spinning tag rides the high segment only. */
|
|
56
|
+
export declare function parseAttentionAnswers(answers: Record<string, {
|
|
57
|
+
value: number;
|
|
58
|
+
}>, id8ToChild: ReadonlyMap<string, string>): [childId: string, score: number, tag: AttentionTag | undefined][];
|
|
59
|
+
export interface AttentionJevOptions {
|
|
60
|
+
/** Live settings read — hot-applies the `jevAttention` toggle with no restart. */
|
|
61
|
+
modeReader: () => 'auto' | 'off';
|
|
62
|
+
env?: NodeJS.ProcessEnv;
|
|
63
|
+
fetchImpl?: typeof fetch;
|
|
64
|
+
now?: () => number;
|
|
65
|
+
minIntervalMs?: number;
|
|
66
|
+
/** Test seam: inject the core module directly, skipping the runtime probe. */
|
|
67
|
+
module?: JevCoreModule;
|
|
68
|
+
}
|
|
69
|
+
export interface AttentionJevSource {
|
|
70
|
+
/**
|
|
71
|
+
* Driven from the bridge's reconcile tick after `board.update(...)`;
|
|
72
|
+
* `cap` is the settings-resolved maxRounds (0 = unlimited) for the state
|
|
73
|
+
* table. Fire-and-forget, self-serializing.
|
|
74
|
+
*/
|
|
75
|
+
tick(board: AttentionBoard, views: readonly AgentView[], cap: number): void;
|
|
76
|
+
/** Failed-call counter (diagnostics; the ranking itself never surfaces errors). */
|
|
77
|
+
failures(): number;
|
|
78
|
+
}
|
|
79
|
+
export declare function createJevAttentionSource(options: AttentionJevOptions): AttentionJevSource;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The jev upgrade layer for attention (ticket 04): batches every dirty
|
|
3
|
+
* live child into ONE System One call — two atomic noul questions per child
|
|
4
|
+
* (`attention_<id8>`: needs-operator-attention, `spin_<id8>`: productive vs
|
|
5
|
+
* circling) over a shared compact state table — and writes the scores back
|
|
6
|
+
* through `AttentionBoard.applyJevScores`. A high-segment `spin` verdict
|
|
7
|
+
* (≥0.75; middle values are uncertainty, never evidence) becomes the
|
|
8
|
+
* `spinning` tag the early-hard-stop ladder later consumes (ticket 05).
|
|
9
|
+
*
|
|
10
|
+
* jev-optional by construction: the source ticks silently when the setting
|
|
11
|
+
* is off, the core probe fails, no key is configured, the debounce window
|
|
12
|
+
* is open, another call is in flight, or nothing is dirty — and a failed
|
|
13
|
+
* call is a silent counter bump, never a warn and never a turned-down
|
|
14
|
+
* ranking (the heuristic layer underneath is always live).
|
|
15
|
+
*/
|
|
16
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
17
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
18
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
19
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return path;
|
|
23
|
+
};
|
|
24
|
+
/** Probe the core once per process; a failed import degrades to heuristic-only ranking. */
|
|
25
|
+
async function loadJevCore() {
|
|
26
|
+
const specifier = '@aiwayds/dsh-jev-core';
|
|
27
|
+
try {
|
|
28
|
+
const mod = (await import(__rewriteRelativeImportExtension(specifier)));
|
|
29
|
+
if (typeof mod['classify'] !== 'function' || typeof mod['isConfigured'] !== 'function')
|
|
30
|
+
return null;
|
|
31
|
+
return mod;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Minimum gap between jev dispatches (ticket 04: global debounce 30s). */
|
|
38
|
+
export const ATTENTION_JEV_MIN_INTERVAL_MS = 30_000;
|
|
39
|
+
/** Per-call deadline: advisory data, generous but bounded. */
|
|
40
|
+
export const ATTENTION_JEV_TIMEOUT_MS = 8_000;
|
|
41
|
+
/** High-segment line for the spin verdict — middle values are uncertainty, not evidence. */
|
|
42
|
+
export const SPINNING_TAG_AT = 0.75;
|
|
43
|
+
/** State table size guard (ticket 04: ~800 chars; hard cap keeps runaway labels bounded). */
|
|
44
|
+
const STATE_MAX_CHARS = 1000;
|
|
45
|
+
const LABEL_MAX_CHARS = 40;
|
|
46
|
+
/** The id8 collision map is first-wins by view order; uuid4 id8 collisions are astronomic. */
|
|
47
|
+
export function buildAttentionQuestions(id8s) {
|
|
48
|
+
const questions = {};
|
|
49
|
+
for (const id8 of id8s) {
|
|
50
|
+
questions[`attention_${id8}`] = {
|
|
51
|
+
type: 'noul',
|
|
52
|
+
criteria: {
|
|
53
|
+
true: 'needs operator attention now — stalled, circling, near its round cap, or ignoring guidance',
|
|
54
|
+
false: 'progressing fine — no intervention warranted',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
questions[`spin_${id8}`] = {
|
|
58
|
+
type: 'noul',
|
|
59
|
+
criteria: {
|
|
60
|
+
true: 'work is UNPRODUCTIVE — repeating the same tools or circling without forward motion',
|
|
61
|
+
false: 'work is productive — measurable forward progress',
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return questions;
|
|
66
|
+
}
|
|
67
|
+
/** The shared state table: one compact line per child, the facts both questions judge. */
|
|
68
|
+
export function buildAttentionState(views, cap, now, stallOf) {
|
|
69
|
+
const lines = views.map((view) => {
|
|
70
|
+
const flags = [];
|
|
71
|
+
if (view.injectedAt !== undefined)
|
|
72
|
+
flags.push('injected');
|
|
73
|
+
if (view.retries > 0)
|
|
74
|
+
flags.push(`retrying ${view.retries}/${view.maxRetries ?? '?'}`);
|
|
75
|
+
const parts = [
|
|
76
|
+
`${view.childId.slice(0, 8)} ${view.label.slice(0, LABEL_MAX_CHARS)}`,
|
|
77
|
+
`rounds ${view.rounds}/${cap > 0 ? cap : '∞'}`,
|
|
78
|
+
`idle ${Math.round(stallOf(view.childId) / 1000)}s`,
|
|
79
|
+
`tok ${view.tokens}`,
|
|
80
|
+
...(view.contextWindow !== undefined && view.contextWindow > 0
|
|
81
|
+
? [`ctx ${Math.round((view.contextTokens / view.contextWindow) * 100)}%`]
|
|
82
|
+
: []),
|
|
83
|
+
...(view.lastTool !== undefined ? [`last=${view.lastTool}`] : []),
|
|
84
|
+
...(flags.length > 0 ? [`flags: ${flags.join(', ')}`] : []),
|
|
85
|
+
];
|
|
86
|
+
return `- ${parts.join(' | ')}`;
|
|
87
|
+
});
|
|
88
|
+
const head = 'Subagents under supervision; judge each child on the two questions keyed by its id.';
|
|
89
|
+
return `${head}\n${lines.join('\n')}`.slice(0, STATE_MAX_CHARS);
|
|
90
|
+
}
|
|
91
|
+
/** Read the verdict map back onto child ids; the spinning tag rides the high segment only. */
|
|
92
|
+
export function parseAttentionAnswers(answers, id8ToChild) {
|
|
93
|
+
const out = [];
|
|
94
|
+
for (const [id8, childId] of id8ToChild) {
|
|
95
|
+
const attention = answers[`attention_${id8}`];
|
|
96
|
+
if (attention === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
const spin = answers[`spin_${id8}`];
|
|
99
|
+
const tag = spin !== undefined && spin.value >= SPINNING_TAG_AT ? 'spinning' : undefined;
|
|
100
|
+
out.push([childId, attention.value, tag]);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
export function createJevAttentionSource(options) {
|
|
105
|
+
const now = options.now ?? Date.now;
|
|
106
|
+
const minIntervalMs = options.minIntervalMs ?? ATTENTION_JEV_MIN_INTERVAL_MS;
|
|
107
|
+
const env = options.env ?? process.env;
|
|
108
|
+
let core;
|
|
109
|
+
let loading;
|
|
110
|
+
let lastDispatchAt = -Infinity;
|
|
111
|
+
let inFlight = false;
|
|
112
|
+
let failureCount = 0;
|
|
113
|
+
const ensureLoaded = () => {
|
|
114
|
+
if (options.module !== undefined)
|
|
115
|
+
return Promise.resolve(options.module);
|
|
116
|
+
if (core !== undefined)
|
|
117
|
+
return Promise.resolve(core);
|
|
118
|
+
loading ??= loadJevCore().then((mod) => {
|
|
119
|
+
core = mod;
|
|
120
|
+
return mod;
|
|
121
|
+
});
|
|
122
|
+
return loading;
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
failures: () => failureCount,
|
|
126
|
+
tick(board, views, cap) {
|
|
127
|
+
if (inFlight)
|
|
128
|
+
return;
|
|
129
|
+
if (options.modeReader() !== 'auto')
|
|
130
|
+
return;
|
|
131
|
+
const live = views.filter(view => view.outcome === undefined);
|
|
132
|
+
const dirty = board.dirtyChildren().filter(childId => live.some(view => view.childId === childId));
|
|
133
|
+
if (dirty.length === 0)
|
|
134
|
+
return;
|
|
135
|
+
const at = now();
|
|
136
|
+
if (at - lastDispatchAt < minIntervalMs)
|
|
137
|
+
return;
|
|
138
|
+
void ensureLoaded().then((mod) => {
|
|
139
|
+
if (mod === null)
|
|
140
|
+
return;
|
|
141
|
+
if (!mod.isConfigured({ env }))
|
|
142
|
+
return;
|
|
143
|
+
const id8ToChild = new Map(live.map(view => [view.childId.slice(0, 8), view.childId]));
|
|
144
|
+
const questions = buildAttentionQuestions([...id8ToChild.keys()]);
|
|
145
|
+
const state = buildAttentionState(live, cap, at, childId => board.rows().find(row => row.childId === childId)?.stallMs ?? 0);
|
|
146
|
+
inFlight = true;
|
|
147
|
+
lastDispatchAt = at;
|
|
148
|
+
mod
|
|
149
|
+
.classify({ questions, state, env, fetchImpl: options.fetchImpl, timeoutMs: ATTENTION_JEV_TIMEOUT_MS })
|
|
150
|
+
.then(result => board.applyJevScores(parseAttentionAnswers(result.answers, id8ToChild)))
|
|
151
|
+
.catch(() => {
|
|
152
|
+
failureCount += 1;
|
|
153
|
+
})
|
|
154
|
+
.finally(() => {
|
|
155
|
+
inFlight = false;
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=attention-jev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-jev.js","sourceRoot":"","sources":["../src/attention-jev.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent attention ranking — the heuristic base layer (Charter #6:
|
|
3
|
+
* jev-optional). Scores every live child 0–1 for "how much does this need
|
|
4
|
+
* the operator's eye" from structured view facts alone (stall, rounds vs
|
|
5
|
+
* cap, unanswered injections, retries, context occupancy); the jev layer
|
|
6
|
+
* (attention-jev.ts) may REPLACE a heuristic judgment with a semantic one
|
|
7
|
+
* and does nothing when unconfigured — the ranking below is always live and
|
|
8
|
+
* costs nothing but a map walk on the bridge's 600ms reconcile tick.
|
|
9
|
+
*
|
|
10
|
+
* Layer contract (ticket 04): a jev judgment is authoritative UNTIL the
|
|
11
|
+
* child makes progress (rounds or tokens move) — jev judged a snapshot, and
|
|
12
|
+
* progress is new state, so the entry falls back to heuristic until the
|
|
13
|
+
* next jev pass covers it. Heuristic and jev scores share the same 0–1
|
|
14
|
+
* scale and tier cuts, so consumers never branch on the layer beyond the
|
|
15
|
+
* `source` label they may display.
|
|
16
|
+
*
|
|
17
|
+
* This file is pure terminal-free logic; `AttentionBoard` holds the only
|
|
18
|
+
* mutable state (per-child observation + the current info map).
|
|
19
|
+
*/
|
|
20
|
+
import type { AgentView } from './dsh-events.ts';
|
|
21
|
+
export type AttentionTier = 'critical' | 'warn' | 'ok';
|
|
22
|
+
export type AttentionSourceKind = 'heuristic' | 'jev';
|
|
23
|
+
/**
|
|
24
|
+
* `stalled` / `near-cap` are heuristic signals (the top contributing
|
|
25
|
+
* factor); `spinning` / `deep-work` / `waiting` are jev's semantic tags and
|
|
26
|
+
* never produced here.
|
|
27
|
+
*/
|
|
28
|
+
export type AttentionTag = 'stalled' | 'near-cap' | 'spinning' | 'deep-work' | 'waiting';
|
|
29
|
+
export interface AttentionInfo {
|
|
30
|
+
readonly score: number;
|
|
31
|
+
readonly tier: AttentionTier;
|
|
32
|
+
readonly source: AttentionSourceKind;
|
|
33
|
+
readonly tag?: AttentionTag;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The spin-streak state the early-stop ladder consumes. `lastStrikeAt` is
|
|
37
|
+
* the clock of the MOST RECENT spinning verdict — the ladder's freshness
|
|
38
|
+
* anchor (a strike older than the stale window, or older than a delivered
|
|
39
|
+
* wrap-up, must not stop anything).
|
|
40
|
+
*/
|
|
41
|
+
export interface SpinState {
|
|
42
|
+
/** Consecutive spinning verdicts (a non-spinning verdict zeroes it). */
|
|
43
|
+
readonly streak: number;
|
|
44
|
+
readonly lastStrikeAt: number;
|
|
45
|
+
}
|
|
46
|
+
/** A spinning verdict older than this is stale evidence — three debounce windows. */
|
|
47
|
+
export declare const SPIN_STRIKE_STALE_MS = 90000;
|
|
48
|
+
/** One consumer-facing row: the view facts plus the current attention info. */
|
|
49
|
+
export interface AttentionRow {
|
|
50
|
+
readonly childId: string;
|
|
51
|
+
readonly label: string;
|
|
52
|
+
readonly info: AttentionInfo;
|
|
53
|
+
/** Ms since the child last made observable progress (rounds/tokens moved). */
|
|
54
|
+
readonly stallMs: number;
|
|
55
|
+
}
|
|
56
|
+
/** Tier cuts — shared by both layers so a jev score and a heuristic score are tier-comparable. */
|
|
57
|
+
export declare const ATTENTION_CRITICAL_AT = 0.6;
|
|
58
|
+
export declare const ATTENTION_WARN_AT = 0.35;
|
|
59
|
+
/** Stall ramp: no penalty under a minute of silence; full weight at ten minutes. */
|
|
60
|
+
export declare const ATTENTION_STALL_FLOOR_MS = 60000;
|
|
61
|
+
export declare const ATTENTION_STALL_CEIL_MS = 600000;
|
|
62
|
+
export interface HeuristicInput {
|
|
63
|
+
/** Ms since observable progress (rounds or tokens moved). */
|
|
64
|
+
stallMs: number;
|
|
65
|
+
/** Assistant-message count; the "rounds" the maxRounds ladder caps. */
|
|
66
|
+
rounds: number;
|
|
67
|
+
/** The effective maxRounds cap; 0 = unlimited (the rounds component drops out). */
|
|
68
|
+
cap: number;
|
|
69
|
+
/** A plugin injection (wrap-up / steer) landed and NO progress followed it. */
|
|
70
|
+
ignoredInjection: boolean;
|
|
71
|
+
retries: number;
|
|
72
|
+
/** Current context occupancy as a 0–1 ratio, when the window is known. */
|
|
73
|
+
contextRatio?: number;
|
|
74
|
+
}
|
|
75
|
+
export declare function tierFor(score: number): AttentionTier;
|
|
76
|
+
/** The heuristic score: weighted view facts, 0–1, two decimals. */
|
|
77
|
+
export declare function heuristicAttentionScore(input: HeuristicInput): number;
|
|
78
|
+
/** Heuristic tag: the one strong signal worth naming, else unnamed. */
|
|
79
|
+
export declare function heuristicTag(input: HeuristicInput, score: number): AttentionTag | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* The mutable ranking. One instance per bridge; `update()` on the reconcile
|
|
82
|
+
* tick is the only required driver. `now` is injectable for tests.
|
|
83
|
+
*/
|
|
84
|
+
export declare class AttentionBoard {
|
|
85
|
+
private readonly observations;
|
|
86
|
+
private now;
|
|
87
|
+
constructor(now?: () => number);
|
|
88
|
+
setClock(now: () => number): void;
|
|
89
|
+
/**
|
|
90
|
+
* Re-observe every live child: detect progress, recompute heuristic info,
|
|
91
|
+
* apply any still-fresh jev judgment, prune settled/gone children. Idempotent
|
|
92
|
+
* and O(n) — safe on every tick.
|
|
93
|
+
*/
|
|
94
|
+
update(views: readonly AgentView[], cap: number): void;
|
|
95
|
+
/**
|
|
96
|
+
* The jev layer's write-in: replaces the heuristic judgment for the named
|
|
97
|
+
* children (same scale, same tiers — ticket 04 "替代非融合"). Children the
|
|
98
|
+
* board no longer tracks are ignored; covered children lose their dirty flag.
|
|
99
|
+
*/
|
|
100
|
+
applyJevScores(scores: readonly (readonly [childId: string, score: number, tag: AttentionTag | undefined])[]): void;
|
|
101
|
+
/**
|
|
102
|
+
* The early-stop ladder's input: consecutive spinning verdicts plus the
|
|
103
|
+
* clock of the most recent one (the freshness anchor). Absent children
|
|
104
|
+
* report zeroed state.
|
|
105
|
+
*/
|
|
106
|
+
spinState(childId: string): SpinState;
|
|
107
|
+
/** Children whose state changed since their last jev judgment (the refresh's work list). */
|
|
108
|
+
dirtyChildren(): string[];
|
|
109
|
+
/** One child's current info, when ranked. */
|
|
110
|
+
get(childId: string): AttentionInfo | undefined;
|
|
111
|
+
/** All rows, highest attention first — the picker/status/guard read side. */
|
|
112
|
+
rows(): AttentionRow[];
|
|
113
|
+
/**
|
|
114
|
+
* The guard's deny-copy hint: the most attention-worthy live child that is
|
|
115
|
+
* actually worth acting on (tier above ok), or undefined when the ranking
|
|
116
|
+
* has nothing to say.
|
|
117
|
+
*/
|
|
118
|
+
topHint(): {
|
|
119
|
+
label: string;
|
|
120
|
+
detail: string;
|
|
121
|
+
} | undefined;
|
|
122
|
+
}
|
package/lib/attention.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagent attention ranking — the heuristic base layer (Charter #6:
|
|
3
|
+
* jev-optional). Scores every live child 0–1 for "how much does this need
|
|
4
|
+
* the operator's eye" from structured view facts alone (stall, rounds vs
|
|
5
|
+
* cap, unanswered injections, retries, context occupancy); the jev layer
|
|
6
|
+
* (attention-jev.ts) may REPLACE a heuristic judgment with a semantic one
|
|
7
|
+
* and does nothing when unconfigured — the ranking below is always live and
|
|
8
|
+
* costs nothing but a map walk on the bridge's 600ms reconcile tick.
|
|
9
|
+
*
|
|
10
|
+
* Layer contract (ticket 04): a jev judgment is authoritative UNTIL the
|
|
11
|
+
* child makes progress (rounds or tokens move) — jev judged a snapshot, and
|
|
12
|
+
* progress is new state, so the entry falls back to heuristic until the
|
|
13
|
+
* next jev pass covers it. Heuristic and jev scores share the same 0–1
|
|
14
|
+
* scale and tier cuts, so consumers never branch on the layer beyond the
|
|
15
|
+
* `source` label they may display.
|
|
16
|
+
*
|
|
17
|
+
* This file is pure terminal-free logic; `AttentionBoard` holds the only
|
|
18
|
+
* mutable state (per-child observation + the current info map).
|
|
19
|
+
*/
|
|
20
|
+
/** A spinning verdict older than this is stale evidence — three debounce windows. */
|
|
21
|
+
export const SPIN_STRIKE_STALE_MS = 90_000;
|
|
22
|
+
/** Tier cuts — shared by both layers so a jev score and a heuristic score are tier-comparable. */
|
|
23
|
+
export const ATTENTION_CRITICAL_AT = 0.6;
|
|
24
|
+
export const ATTENTION_WARN_AT = 0.35;
|
|
25
|
+
/** Stall ramp: no penalty under a minute of silence; full weight at ten minutes. */
|
|
26
|
+
export const ATTENTION_STALL_FLOOR_MS = 60_000;
|
|
27
|
+
export const ATTENTION_STALL_CEIL_MS = 600_000;
|
|
28
|
+
/** Component weights — documented so preview data can tune them deliberately. */
|
|
29
|
+
const W_STALL = 0.45;
|
|
30
|
+
const W_ROUNDS = 0.25;
|
|
31
|
+
const W_IGNORED = 0.15;
|
|
32
|
+
const W_RETRIES = 0.1;
|
|
33
|
+
const W_CONTEXT = 0.05;
|
|
34
|
+
function clamp01(value) {
|
|
35
|
+
return Math.min(1, Math.max(0, value));
|
|
36
|
+
}
|
|
37
|
+
function stallFactor(stallMs) {
|
|
38
|
+
if (stallMs <= ATTENTION_STALL_FLOOR_MS)
|
|
39
|
+
return 0;
|
|
40
|
+
return clamp01((stallMs - ATTENTION_STALL_FLOOR_MS) / (ATTENTION_STALL_CEIL_MS - ATTENTION_STALL_FLOOR_MS));
|
|
41
|
+
}
|
|
42
|
+
export function tierFor(score) {
|
|
43
|
+
if (score >= ATTENTION_CRITICAL_AT)
|
|
44
|
+
return 'critical';
|
|
45
|
+
if (score >= ATTENTION_WARN_AT)
|
|
46
|
+
return 'warn';
|
|
47
|
+
return 'ok';
|
|
48
|
+
}
|
|
49
|
+
/** The heuristic score: weighted view facts, 0–1, two decimals. */
|
|
50
|
+
export function heuristicAttentionScore(input) {
|
|
51
|
+
const stall = W_STALL * stallFactor(input.stallMs);
|
|
52
|
+
const rounds = input.cap > 0 ? W_ROUNDS * clamp01(input.rounds / input.cap) : 0;
|
|
53
|
+
const ignored = input.ignoredInjection ? W_IGNORED : 0;
|
|
54
|
+
const retries = W_RETRIES * clamp01(input.retries / 3);
|
|
55
|
+
const context = input.contextRatio !== undefined ? W_CONTEXT * clamp01(input.contextRatio) : 0;
|
|
56
|
+
return Math.round((stall + rounds + ignored + retries + context) * 100) / 100;
|
|
57
|
+
}
|
|
58
|
+
/** Heuristic tag: the one strong signal worth naming, else unnamed. */
|
|
59
|
+
export function heuristicTag(input, score) {
|
|
60
|
+
if (input.cap > 0 && input.rounds / input.cap >= 0.85)
|
|
61
|
+
return 'near-cap';
|
|
62
|
+
if (stallFactor(input.stallMs) >= 0.5)
|
|
63
|
+
return 'stalled';
|
|
64
|
+
// A score that reached warn/critical without a dominant single factor is
|
|
65
|
+
// still worth flagging as stalled-ish for consumers that filter on tags.
|
|
66
|
+
if (score >= ATTENTION_WARN_AT)
|
|
67
|
+
return 'stalled';
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
/** Heuristic inputs derived from one live view + the board's own observation of it. */
|
|
71
|
+
function inputsFor(view, observation, now) {
|
|
72
|
+
return {
|
|
73
|
+
stallMs: Math.max(0, now - observation.lastProgressAt),
|
|
74
|
+
rounds: view.rounds,
|
|
75
|
+
cap: 0, // substituted by the board (settings-resolved)
|
|
76
|
+
ignoredInjection: view.injectedAt !== undefined && observation.lastProgressAt <= view.injectedAt,
|
|
77
|
+
retries: view.retries,
|
|
78
|
+
contextRatio: view.contextWindow !== undefined && view.contextWindow > 0
|
|
79
|
+
? view.contextTokens / view.contextWindow
|
|
80
|
+
: undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The mutable ranking. One instance per bridge; `update()` on the reconcile
|
|
85
|
+
* tick is the only required driver. `now` is injectable for tests.
|
|
86
|
+
*/
|
|
87
|
+
export class AttentionBoard {
|
|
88
|
+
observations = new Map();
|
|
89
|
+
now;
|
|
90
|
+
constructor(now = Date.now) {
|
|
91
|
+
this.now = now;
|
|
92
|
+
}
|
|
93
|
+
setClock(now) {
|
|
94
|
+
this.now = now;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Re-observe every live child: detect progress, recompute heuristic info,
|
|
98
|
+
* apply any still-fresh jev judgment, prune settled/gone children. Idempotent
|
|
99
|
+
* and O(n) — safe on every tick.
|
|
100
|
+
*/
|
|
101
|
+
update(views, cap) {
|
|
102
|
+
const now = this.now();
|
|
103
|
+
// The board ranks LIVE children only: a child that settled this pass is
|
|
104
|
+
// pruned with the same stroke (its observation must not linger stale).
|
|
105
|
+
const live = new Set(views.filter(view => view.outcome === undefined).map(view => view.childId));
|
|
106
|
+
for (const childId of [...this.observations.keys()]) {
|
|
107
|
+
if (!live.has(childId))
|
|
108
|
+
this.observations.delete(childId);
|
|
109
|
+
}
|
|
110
|
+
for (const view of views) {
|
|
111
|
+
if (view.outcome !== undefined || !live.has(view.childId))
|
|
112
|
+
continue;
|
|
113
|
+
const previous = this.observations.get(view.childId);
|
|
114
|
+
const progressed = previous === undefined ||
|
|
115
|
+
view.rounds !== previous.lastRounds ||
|
|
116
|
+
view.tokens !== previous.lastTokens;
|
|
117
|
+
const observation = previous ?? {
|
|
118
|
+
label: view.label,
|
|
119
|
+
lastProgressAt: now,
|
|
120
|
+
lastRounds: view.rounds,
|
|
121
|
+
lastTokens: view.tokens,
|
|
122
|
+
info: { score: 0, tier: 'ok', source: 'heuristic' },
|
|
123
|
+
dirtyForJev: true,
|
|
124
|
+
spinStreak: 0,
|
|
125
|
+
lastStrikeAt: 0,
|
|
126
|
+
};
|
|
127
|
+
observation.label = view.label;
|
|
128
|
+
if (progressed) {
|
|
129
|
+
observation.lastProgressAt = now;
|
|
130
|
+
observation.lastRounds = view.rounds;
|
|
131
|
+
observation.lastTokens = view.tokens;
|
|
132
|
+
observation.dirtyForJev = true;
|
|
133
|
+
// A jev judgment judged the state BEFORE this progress — the info
|
|
134
|
+
// falls back to heuristic (below). The spin streak is deliberately
|
|
135
|
+
// NOT cleared: a circling child makes "progress" (rounds grow) by
|
|
136
|
+
// definition, and freshness is the ladder's job (lastStrikeAt).
|
|
137
|
+
}
|
|
138
|
+
const input = { ...inputsFor(view, observation, now), cap };
|
|
139
|
+
const score = heuristicAttentionScore(input);
|
|
140
|
+
observation.info = {
|
|
141
|
+
score,
|
|
142
|
+
tier: tierFor(score),
|
|
143
|
+
source: 'heuristic',
|
|
144
|
+
tag: heuristicTag(input, score),
|
|
145
|
+
};
|
|
146
|
+
this.observations.set(view.childId, observation);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The jev layer's write-in: replaces the heuristic judgment for the named
|
|
151
|
+
* children (same scale, same tiers — ticket 04 "替代非融合"). Children the
|
|
152
|
+
* board no longer tracks are ignored; covered children lose their dirty flag.
|
|
153
|
+
*/
|
|
154
|
+
applyJevScores(scores) {
|
|
155
|
+
const now = this.now();
|
|
156
|
+
for (const [childId, score, tag] of scores) {
|
|
157
|
+
const observation = this.observations.get(childId);
|
|
158
|
+
if (observation === undefined)
|
|
159
|
+
continue;
|
|
160
|
+
const clamped = clamp01(score);
|
|
161
|
+
observation.info = { score: clamped, tier: tierFor(clamped), source: 'jev', ...(tag !== undefined ? { tag } : {}) };
|
|
162
|
+
observation.dirtyForJev = false;
|
|
163
|
+
if (tag === 'spinning') {
|
|
164
|
+
observation.spinStreak += 1;
|
|
165
|
+
observation.lastStrikeAt = now;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
observation.spinStreak = 0;
|
|
169
|
+
observation.lastStrikeAt = 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The early-stop ladder's input: consecutive spinning verdicts plus the
|
|
175
|
+
* clock of the most recent one (the freshness anchor). Absent children
|
|
176
|
+
* report zeroed state.
|
|
177
|
+
*/
|
|
178
|
+
spinState(childId) {
|
|
179
|
+
const observation = this.observations.get(childId);
|
|
180
|
+
return observation === undefined
|
|
181
|
+
? { streak: 0, lastStrikeAt: 0 }
|
|
182
|
+
: { streak: observation.spinStreak, lastStrikeAt: observation.lastStrikeAt };
|
|
183
|
+
}
|
|
184
|
+
/** Children whose state changed since their last jev judgment (the refresh's work list). */
|
|
185
|
+
dirtyChildren() {
|
|
186
|
+
return [...this.observations.entries()].filter(([, observation]) => observation.dirtyForJev).map(([childId]) => childId);
|
|
187
|
+
}
|
|
188
|
+
/** One child's current info, when ranked. */
|
|
189
|
+
get(childId) {
|
|
190
|
+
return this.observations.get(childId)?.info;
|
|
191
|
+
}
|
|
192
|
+
/** All rows, highest attention first — the picker/status/guard read side. */
|
|
193
|
+
rows() {
|
|
194
|
+
const now = this.now();
|
|
195
|
+
return [...this.observations.entries()]
|
|
196
|
+
.map(([childId, observation]) => ({
|
|
197
|
+
childId,
|
|
198
|
+
label: observation.label,
|
|
199
|
+
info: observation.info,
|
|
200
|
+
stallMs: Math.max(0, now - observation.lastProgressAt),
|
|
201
|
+
}))
|
|
202
|
+
.sort((a, b) => b.info.score - a.info.score);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* The guard's deny-copy hint: the most attention-worthy live child that is
|
|
206
|
+
* actually worth acting on (tier above ok), or undefined when the ranking
|
|
207
|
+
* has nothing to say.
|
|
208
|
+
*/
|
|
209
|
+
topHint() {
|
|
210
|
+
const row = this.rows().find(row => row.info.tier !== 'ok');
|
|
211
|
+
if (row === undefined)
|
|
212
|
+
return undefined;
|
|
213
|
+
const tag = row.info.tag !== undefined ? `, ${row.info.tag}` : '';
|
|
214
|
+
return {
|
|
215
|
+
label: row.label,
|
|
216
|
+
detail: `${row.info.source} score ${row.info.score.toFixed(2)}${tag}, idle ${Math.round(row.stallMs / 1000)}s`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=attention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention.js","sourceRoot":"","sources":["../src/attention.ts"],"names":[],"mappings":"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"}
|
package/lib/dsh-events.d.ts
CHANGED
|
@@ -197,14 +197,17 @@ export interface AgentView {
|
|
|
197
197
|
readonly injectedAt?: number;
|
|
198
198
|
/**
|
|
199
199
|
* Set when the maxRounds policy HARD-STOPPED this child (the wrap-up's
|
|
200
|
-
* grace window exhausted
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
200
|
+
* grace window exhausted, or the spin-detected early stop): the round
|
|
201
|
+
* count at the stop and the cap it exceeded. `early` marks a stop fired
|
|
202
|
+
* BEFORE the grace window — the attention ranking judged the child
|
|
203
|
+
* circling twice. Surfaced as the `⏻` marker on the compact line, the
|
|
204
|
+
* picker row and the viewer transcript (a marker row) — a force-stop is
|
|
205
|
+
* policy, not a failure, and must be visible as such.
|
|
204
206
|
*/
|
|
205
207
|
readonly hardStop?: {
|
|
206
208
|
readonly round: number;
|
|
207
209
|
readonly cap: number;
|
|
210
|
+
readonly early?: true;
|
|
208
211
|
};
|
|
209
212
|
/**
|
|
210
213
|
* Latest visible last line of the child's own CONTENT output (the tail the
|