@hmharness/agent 0.23.22 → 0.23.24
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/code-wm-live.d.ts +44 -0
- package/dist/code-wm-live.js +72 -0
- package/dist/code-wm-recorder.d.ts +23 -0
- package/dist/code-wm-recorder.js +71 -0
- package/dist/code-wm-sensor.d.ts +41 -0
- package/dist/code-wm-sensor.js +69 -0
- package/dist/extension-wm-sensor.d.ts +32 -0
- package/dist/extension-wm-sensor.js +21 -0
- package/dist/i18n.d.ts +4 -0
- package/dist/i18n.js +6 -0
- package/dist/index.d.ts +13 -13
- package/dist/runner.d.ts +2 -1
- package/dist/runner.js +20 -0
- package/package.json +11 -10
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CodeWorldModel } from '@hmharness/cognitive';
|
|
2
|
+
import type { LspClient, Diagnostic } from '@hmharness/lsp';
|
|
3
|
+
export interface LiveCodeWmSensorOptions {
|
|
4
|
+
workspaceRoot: string;
|
|
5
|
+
languageId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class LiveCodeWmSensor {
|
|
8
|
+
private client;
|
|
9
|
+
private cwm;
|
|
10
|
+
private opts;
|
|
11
|
+
private diagPushes;
|
|
12
|
+
private lastSyncAt;
|
|
13
|
+
private tracked;
|
|
14
|
+
constructor(client: LspClient, cwm: CodeWorldModel, opts: LiveCodeWmSensorOptions);
|
|
15
|
+
/**
|
|
16
|
+
* Full sync for one file: open the doc on the server (triggers analysis),
|
|
17
|
+
* pull symbols, pull diagnostics from the model's sensor state, push both
|
|
18
|
+
* into the Code WM. Returns the ingested counts.
|
|
19
|
+
*/
|
|
20
|
+
syncFile(absPath: string, text: string): Promise<{
|
|
21
|
+
entitiesIngested: number;
|
|
22
|
+
relationsIngested: number;
|
|
23
|
+
diagnosticsIngested: number;
|
|
24
|
+
}>;
|
|
25
|
+
/** Pull the CURRENT diagnostics for a uri from the model's sensor state.
|
|
26
|
+
* Range-restored: the Code WM keeps message-level state (no ranges);
|
|
27
|
+
* zero ranges are the honest placeholder for sensor re-reads, never a
|
|
28
|
+
* fabricated location. */
|
|
29
|
+
pullDiagnostics(uri: string): Diagnostic[];
|
|
30
|
+
get stats(): {
|
|
31
|
+
tracked: number;
|
|
32
|
+
diagPushes: number;
|
|
33
|
+
lastSyncAt: string | null;
|
|
34
|
+
};
|
|
35
|
+
/** Sync many files (workspace scan path). */
|
|
36
|
+
syncFiles(files: Array<{
|
|
37
|
+
path: string;
|
|
38
|
+
text: string;
|
|
39
|
+
}>): Promise<{
|
|
40
|
+
entitiesIngested: number;
|
|
41
|
+
relationsIngested: number;
|
|
42
|
+
diagnosticsIngested: number;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/agent - live LSP sensor → Code World Model bridge (audit line B)
|
|
3
|
+
*
|
|
4
|
+
* The mapping functions (lspSymbolsToOntology etc.) are pure; THIS class
|
|
5
|
+
* makes them LIVE: an LspClient's push stream (diagnostics) and pull
|
|
6
|
+
* results (documentSymbols) flow into a CodeWorldModel continuously, with
|
|
7
|
+
* openDoc() driving the server to analyze the file in the first place.
|
|
8
|
+
*
|
|
9
|
+
* Usage: create with a connected LspClient + a CodeWorldModel + the
|
|
10
|
+
* workspace root; call syncFile(uri, text) after each edit the agent makes
|
|
11
|
+
* — the model then carries a current code world for the slice/MEA/digest
|
|
12
|
+
* layers to reason over.
|
|
13
|
+
*/
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
import { fileToUri } from '@hmharness/lsp';
|
|
16
|
+
import { syncCodeWorldModel } from "./code-wm-sensor.js";
|
|
17
|
+
export class LiveCodeWmSensor {
|
|
18
|
+
client;
|
|
19
|
+
cwm;
|
|
20
|
+
opts;
|
|
21
|
+
diagPushes = 0;
|
|
22
|
+
lastSyncAt = null;
|
|
23
|
+
tracked = new Set();
|
|
24
|
+
constructor(client, cwm, opts) {
|
|
25
|
+
this.client = client;
|
|
26
|
+
this.cwm = cwm;
|
|
27
|
+
this.opts = opts;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Full sync for one file: open the doc on the server (triggers analysis),
|
|
31
|
+
* pull symbols, pull diagnostics from the model's sensor state, push both
|
|
32
|
+
* into the Code WM. Returns the ingested counts.
|
|
33
|
+
*/
|
|
34
|
+
async syncFile(absPath, text) {
|
|
35
|
+
const uri = fileToUri(absPath);
|
|
36
|
+
this.client.openDoc(uri, this.opts.languageId ?? 'typescript', text);
|
|
37
|
+
const symbols = await this.client.documentSymbols({ uri }).catch(() => []);
|
|
38
|
+
const diagnostics = this.pullDiagnostics(uri);
|
|
39
|
+
const result = syncCodeWorldModel(this.cwm, { uri, symbols, diagnostics });
|
|
40
|
+
this.diagPushes += result.diagnosticsIngested;
|
|
41
|
+
this.lastSyncAt = new Date().toISOString();
|
|
42
|
+
this.tracked.add(uri);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
/** Pull the CURRENT diagnostics for a uri from the model's sensor state.
|
|
46
|
+
* Range-restored: the Code WM keeps message-level state (no ranges);
|
|
47
|
+
* zero ranges are the honest placeholder for sensor re-reads, never a
|
|
48
|
+
* fabricated location. */
|
|
49
|
+
pullDiagnostics(uri) {
|
|
50
|
+
const diags = this.cwm.diagnosticsFor(uri);
|
|
51
|
+
return diags.map((d) => ({
|
|
52
|
+
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
|
|
53
|
+
severity: (d.severity ?? 1),
|
|
54
|
+
message: d.message,
|
|
55
|
+
source: 'cwm',
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
get stats() {
|
|
59
|
+
return { tracked: this.tracked.size, diagPushes: this.diagPushes, lastSyncAt: this.lastSyncAt };
|
|
60
|
+
}
|
|
61
|
+
/** Sync many files (workspace scan path). */
|
|
62
|
+
async syncFiles(files) {
|
|
63
|
+
const total = { entitiesIngested: 0, relationsIngested: 0, diagnosticsIngested: 0 };
|
|
64
|
+
for (const f of files) {
|
|
65
|
+
const r = await this.syncFile(join(this.opts.workspaceRoot, f.path), f.text);
|
|
66
|
+
total.entitiesIngested += r.entitiesIngested;
|
|
67
|
+
total.relationsIngested += r.relationsIngested;
|
|
68
|
+
total.diagnosticsIngested += r.diagnosticsIngested;
|
|
69
|
+
}
|
|
70
|
+
return total;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/agent - Code World Model recorder (production sensor wiring)
|
|
3
|
+
*
|
|
4
|
+
* Round 38 closes the loop the sensors were waiting for: extension page
|
|
5
|
+
* observations now reach a PERSISTED Code World Model. The recorder is a
|
|
6
|
+
* lazy per-home singleton — the first observation loads the model from
|
|
7
|
+
* HMH_HOME, the sensor feeds it, and a debounce flushes to disk. The LSP
|
|
8
|
+
* sensor (round 35) rides the same store once its tools gain an event
|
|
9
|
+
* hook; until then it stays library-only, honestly.
|
|
10
|
+
*
|
|
11
|
+
* Failure posture: everything best-effort — a storage error logs nothing
|
|
12
|
+
* and never breaks the tool that observed the page (evidence must not be
|
|
13
|
+
* able to kill the work it evidences). A crash loses at most one debounce
|
|
14
|
+
* window of observations.
|
|
15
|
+
*/
|
|
16
|
+
import { type CodeWorldModel } from '@hmharness/cognitive';
|
|
17
|
+
import type { RawPageData } from '@hmharness/extension';
|
|
18
|
+
/** The onPageRead hook handed to extensionTools — one closure per home. */
|
|
19
|
+
export declare function pageReadSink(home: string): (raw: RawPageData) => void;
|
|
20
|
+
/** Test isolation: drop caches + pending timers (never used in production). */
|
|
21
|
+
export declare function resetCodeWorldRecorder(): void;
|
|
22
|
+
/** Introspection for tests/status surfaces: the live model for a home. */
|
|
23
|
+
export declare function liveCodeWorldModel(home: string): Promise<CodeWorldModel | null>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/agent - Code World Model recorder (production sensor wiring)
|
|
3
|
+
*
|
|
4
|
+
* Round 38 closes the loop the sensors were waiting for: extension page
|
|
5
|
+
* observations now reach a PERSISTED Code World Model. The recorder is a
|
|
6
|
+
* lazy per-home singleton — the first observation loads the model from
|
|
7
|
+
* HMH_HOME, the sensor feeds it, and a debounce flushes to disk. The LSP
|
|
8
|
+
* sensor (round 35) rides the same store once its tools gain an event
|
|
9
|
+
* hook; until then it stays library-only, honestly.
|
|
10
|
+
*
|
|
11
|
+
* Failure posture: everything best-effort — a storage error logs nothing
|
|
12
|
+
* and never breaks the tool that observed the page (evidence must not be
|
|
13
|
+
* able to kill the work it evidences). A crash loses at most one debounce
|
|
14
|
+
* window of observations.
|
|
15
|
+
*/
|
|
16
|
+
import { loadCodeWorldModel, saveCodeWorldModel } from '@hmharness/cognitive';
|
|
17
|
+
import { syncExtensionRuntime } from "./extension-wm-sensor.js";
|
|
18
|
+
const SAVE_DEBOUNCE_MS = 2_000;
|
|
19
|
+
const entries = new Map();
|
|
20
|
+
/** The onPageRead hook handed to extensionTools — one closure per home. */
|
|
21
|
+
export function pageReadSink(home) {
|
|
22
|
+
return (raw) => {
|
|
23
|
+
void observe(home, raw);
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function observe(home, raw) {
|
|
27
|
+
try {
|
|
28
|
+
let entry = entries.get(home);
|
|
29
|
+
if (!entry) {
|
|
30
|
+
entry = { load: loadCodeWorldModel(home), dirty: false, timer: null };
|
|
31
|
+
entries.set(home, entry);
|
|
32
|
+
}
|
|
33
|
+
const cwm = await entry.load;
|
|
34
|
+
syncExtensionRuntime(cwm, raw);
|
|
35
|
+
entry.dirty = true;
|
|
36
|
+
if (!entry.timer) {
|
|
37
|
+
entry.timer = setTimeout(() => {
|
|
38
|
+
if (!entry)
|
|
39
|
+
return;
|
|
40
|
+
entry.timer = null;
|
|
41
|
+
void flush(home);
|
|
42
|
+
}, SAVE_DEBOUNCE_MS);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch { /* observation is best-effort by contract */ }
|
|
46
|
+
}
|
|
47
|
+
async function flush(home) {
|
|
48
|
+
const entry = entries.get(home);
|
|
49
|
+
if (!entry || !entry.dirty)
|
|
50
|
+
return;
|
|
51
|
+
entry.dirty = false;
|
|
52
|
+
try {
|
|
53
|
+
const cwm = await entry.load;
|
|
54
|
+
await saveCodeWorldModel(home, cwm);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
entry.dirty = true; // keep the evidence for the next flush
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Test isolation: drop caches + pending timers (never used in production). */
|
|
61
|
+
export function resetCodeWorldRecorder() {
|
|
62
|
+
for (const [, e] of entries)
|
|
63
|
+
if (e.timer)
|
|
64
|
+
clearTimeout(e.timer);
|
|
65
|
+
entries.clear();
|
|
66
|
+
}
|
|
67
|
+
/** Introspection for tests/status surfaces: the live model for a home. */
|
|
68
|
+
export async function liveCodeWorldModel(home) {
|
|
69
|
+
const entry = entries.get(home);
|
|
70
|
+
return entry ? entry.load : null;
|
|
71
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/agent - LSP → Code World Model sensor wiring (audit line B)
|
|
3
|
+
*
|
|
4
|
+
* "LSP 提供 symbol/definition/reference/... 结构化观察,可作为 Code World
|
|
5
|
+
* Model 的传感器;不要把它做成 IDE" (02 doc). This adapter lives in agent
|
|
6
|
+
* — the composition layer that already depends on BOTH @hmharness/lsp and
|
|
7
|
+
* @hmharness/cognitive — so neither layering direction is violated.
|
|
8
|
+
*
|
|
9
|
+
* Mapping (LSP 3.18 shapes → audit ontology):
|
|
10
|
+
* DocumentSymbol tree → CodeEntity per symbol + 'defines' relation per
|
|
11
|
+
* parent→child edge (the only relation LSP symbols PROVE; calls/
|
|
12
|
+
* references need deeper requests — later sensors)
|
|
13
|
+
* publishDiagnostics payload → CodeDiagnostic (source: 'lsp' — the
|
|
14
|
+
* feedback-not-proof label travels into the model)
|
|
15
|
+
* BuildFact/RuntimeFact have their own sensors (build tool, runs) —
|
|
16
|
+
* not this adapter's job.
|
|
17
|
+
*/
|
|
18
|
+
import { CodeWorldModel, type CodeDiagnostic, type CodeEntity, type SymbolRelation } from '@hmharness/cognitive';
|
|
19
|
+
import type { Diagnostic, DocumentSymbol } from '@hmharness/lsp';
|
|
20
|
+
/**
|
|
21
|
+
* LSP DocumentSymbol tree → entities + 'defines' relations. Nested children
|
|
22
|
+
* become defines edges (a document defines its top-level symbols; a class
|
|
23
|
+
* defines its methods). Only what the symbols response PROVES is emitted.
|
|
24
|
+
*/
|
|
25
|
+
export declare function lspSymbolsToOntology(uri: string, symbols: DocumentSymbol[]): {
|
|
26
|
+
entities: CodeEntity[];
|
|
27
|
+
relations: SymbolRelation[];
|
|
28
|
+
};
|
|
29
|
+
/** LSP diagnostics payload → audit CodeDiagnostic (source pinned to 'lsp'). */
|
|
30
|
+
export declare function lspDiagnosticsToOntology(uri: string, diagnostics: Diagnostic[]): CodeDiagnostic[];
|
|
31
|
+
export interface CodeWorldSyncInput {
|
|
32
|
+
uri: string;
|
|
33
|
+
symbols?: DocumentSymbol[];
|
|
34
|
+
diagnostics?: Diagnostic[];
|
|
35
|
+
}
|
|
36
|
+
/** One sensor sync: symbols + diagnostics from LSP into the model. */
|
|
37
|
+
export declare function syncCodeWorldModel(cwm: CodeWorldModel, input: CodeWorldSyncInput): {
|
|
38
|
+
entitiesIngested: number;
|
|
39
|
+
relationsIngested: number;
|
|
40
|
+
diagnosticsIngested: number;
|
|
41
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** Map a LSP symbol kind number to the audit's entity kind (best effort). */
|
|
2
|
+
function entityKind(symbolKind) {
|
|
3
|
+
// LTS subset of LSP SymbolKind; unknown kinds land as 'field' — honest
|
|
4
|
+
// coarseness beats a wrong specific kind (the audit ontology has no
|
|
5
|
+
// 'property' kind — methods/fields cover the LSP property case)
|
|
6
|
+
if (symbolKind === 12)
|
|
7
|
+
return 'function';
|
|
8
|
+
if (symbolKind === 5)
|
|
9
|
+
return 'class';
|
|
10
|
+
if (symbolKind === 6)
|
|
11
|
+
return 'method';
|
|
12
|
+
if (symbolKind === 2)
|
|
13
|
+
return 'module';
|
|
14
|
+
if (symbolKind === 1)
|
|
15
|
+
return 'file';
|
|
16
|
+
return 'field';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* LSP DocumentSymbol tree → entities + 'defines' relations. Nested children
|
|
20
|
+
* become defines edges (a document defines its top-level symbols; a class
|
|
21
|
+
* defines its methods). Only what the symbols response PROVES is emitted.
|
|
22
|
+
*/
|
|
23
|
+
export function lspSymbolsToOntology(uri, symbols) {
|
|
24
|
+
const entities = [];
|
|
25
|
+
const relations = [];
|
|
26
|
+
const walk = (list, parentId) => {
|
|
27
|
+
for (const s of list) {
|
|
28
|
+
const id = `${uri}#${s.name}`;
|
|
29
|
+
entities.push({
|
|
30
|
+
id,
|
|
31
|
+
kind: entityKind(s.kind),
|
|
32
|
+
name: s.name,
|
|
33
|
+
uri,
|
|
34
|
+
...(s.range?.start ? { range: { startLine: s.range.start.line + 1, endLine: (s.range.end?.line ?? s.range.start.line) + 1 } } : {}),
|
|
35
|
+
});
|
|
36
|
+
if (parentId)
|
|
37
|
+
relations.push({ from: parentId, to: id, kind: 'defines' });
|
|
38
|
+
if (s.children?.length)
|
|
39
|
+
walk(s.children, id);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
walk(symbols, null);
|
|
43
|
+
return { entities, relations };
|
|
44
|
+
}
|
|
45
|
+
/** LSP diagnostics payload → audit CodeDiagnostic (source pinned to 'lsp'). */
|
|
46
|
+
export function lspDiagnosticsToOntology(uri, diagnostics) {
|
|
47
|
+
return diagnostics.map((d) => ({
|
|
48
|
+
uri,
|
|
49
|
+
severity: (d.severity ?? 1),
|
|
50
|
+
message: d.message,
|
|
51
|
+
source: 'lsp', // the feedback-not-proof label rides into the model
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
/** One sensor sync: symbols + diagnostics from LSP into the model. */
|
|
55
|
+
export function syncCodeWorldModel(cwm, input) {
|
|
56
|
+
const result = { entitiesIngested: 0, relationsIngested: 0, diagnosticsIngested: 0 };
|
|
57
|
+
if (input.symbols?.length) {
|
|
58
|
+
const { entities, relations } = lspSymbolsToOntology(input.uri, input.symbols);
|
|
59
|
+
cwm.ingest({ entities, relations });
|
|
60
|
+
result.entitiesIngested = entities.length;
|
|
61
|
+
result.relationsIngested = relations.length;
|
|
62
|
+
}
|
|
63
|
+
if (input.diagnostics) {
|
|
64
|
+
const diags = lspDiagnosticsToOntology(input.uri, input.diagnostics);
|
|
65
|
+
cwm.ingest({ diagnostics: diags });
|
|
66
|
+
result.diagnosticsIngested = diags.length;
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/agent - browser-extension → Code World Model runtime sensor
|
|
3
|
+
*
|
|
4
|
+
* The audit ontology's RuntimeFact ("runs" sensor) gets its first source:
|
|
5
|
+
* the user's REAL browser. Every extension_page_read observation lands as
|
|
6
|
+
* runtime facts — where the agent looked and what the user had selected.
|
|
7
|
+
* Like the LSP sensor (code-wm-sensor.ts, round 35), this adapter lives
|
|
8
|
+
* in agent — the composition layer that already depends on BOTH
|
|
9
|
+
* @hmharness/extension and @hmharness/cognitive — so neither layering
|
|
10
|
+
* direction is violated, and the mapping is a library call waiting for
|
|
11
|
+
* the model's persistence wiring (same honest posture as round 35).
|
|
12
|
+
*
|
|
13
|
+
* Mapping discipline (honest, bounded):
|
|
14
|
+
* every page read → ONE RuntimeFact kind 'ext.page.read'
|
|
15
|
+
* (title — url, capped; the identity is the URL)
|
|
16
|
+
* non-empty selection → ONE kind 'ext.page.selection' (user-intent
|
|
17
|
+
* signal — the human highlighted this text while
|
|
18
|
+
* the agent was working)
|
|
19
|
+
* nothing else → headings/links/text are CONTEXT for the loop's
|
|
20
|
+
* turn, not durable facts; flooding the model with
|
|
21
|
+
* per-heading facts would be noise dressed as
|
|
22
|
+
* evidence.
|
|
23
|
+
*/
|
|
24
|
+
import { CodeWorldModel, type RuntimeFact } from '@hmharness/cognitive';
|
|
25
|
+
import type { RawPageData } from '@hmharness/extension';
|
|
26
|
+
/** Page observation → audit RuntimeFacts (pure; no DOM, no I/O). */
|
|
27
|
+
export declare function pageReadToRuntimeFacts(raw: RawPageData, at?: string): RuntimeFact[];
|
|
28
|
+
export interface RuntimeSyncResult {
|
|
29
|
+
factsIngested: number;
|
|
30
|
+
}
|
|
31
|
+
/** One sensor sync: extension page observation → runtime facts. */
|
|
32
|
+
export declare function syncExtensionRuntime(cwm: CodeWorldModel, raw: RawPageData): RuntimeSyncResult;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const clip = (s, n) => {
|
|
2
|
+
const t = String(s ?? '').replace(/\s+/g, ' ').trim();
|
|
3
|
+
return t.length > n ? t.slice(0, n) : t;
|
|
4
|
+
};
|
|
5
|
+
/** Page observation → audit RuntimeFacts (pure; no DOM, no I/O). */
|
|
6
|
+
export function pageReadToRuntimeFacts(raw, at = new Date().toISOString()) {
|
|
7
|
+
const title = clip(raw.title, 120);
|
|
8
|
+
const url = clip(raw.url, 300);
|
|
9
|
+
const facts = [{ kind: 'ext.page.read', detail: title ? `${title} — ${url}` : url, at }];
|
|
10
|
+
const selection = clip(raw.selection, 200);
|
|
11
|
+
if (selection)
|
|
12
|
+
facts.push({ kind: 'ext.page.selection', detail: selection, at });
|
|
13
|
+
return facts;
|
|
14
|
+
}
|
|
15
|
+
/** One sensor sync: extension page observation → runtime facts. */
|
|
16
|
+
export function syncExtensionRuntime(cwm, raw) {
|
|
17
|
+
const facts = pageReadToRuntimeFacts(raw);
|
|
18
|
+
for (const f of facts)
|
|
19
|
+
cwm.ingest({ runtime: f });
|
|
20
|
+
return { factsIngested: facts.length };
|
|
21
|
+
}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -139,6 +139,10 @@ export interface Strings {
|
|
|
139
139
|
opsHint: string;
|
|
140
140
|
tuiLongOutputHint: (tool: string, lines: number) => string;
|
|
141
141
|
tuiUserFolded: (hidden: number) => string;
|
|
142
|
+
/** update briefing trio (T27-v2): start line, completion line, first-launch note */
|
|
143
|
+
tuiUpdating: (v: string) => string;
|
|
144
|
+
tuiUpdated: (v: string) => string;
|
|
145
|
+
tuiBriefing: (v: string, note: string) => string;
|
|
142
146
|
tuiAutoUpdateFailed: (why: string) => string;
|
|
143
147
|
tuiEvolveDone: (proposals: number, insights: number, notes: number) => string;
|
|
144
148
|
tuiPassRate: (pct: string) => string;
|
package/dist/i18n.js
CHANGED
|
@@ -114,6 +114,9 @@ const zh = {
|
|
|
114
114
|
opsHint: '更多操作:/ops scan 立即扫描并生成今日简报 · /ops brief 查看最近简报',
|
|
115
115
|
tuiLongOutputHint: (tool, lines) => `⎿ ${tool} 输出 ${lines} 行(已折叠) · 按 z 展开最后一条 · Ctrl+T 看全文`,
|
|
116
116
|
tuiUserFolded: (hidden) => `(…还有 ${hidden} 行已折叠 · Ctrl+T 看全文)`,
|
|
117
|
+
tuiUpdating: (v) => `⤴ 发现新版本 ${v},后台更新中(不打扰当前会话,新版本下次启动生效)`,
|
|
118
|
+
tuiUpdated: (v) => `✓ 已更新到 ${v} · 重启 hmh 后生效`,
|
|
119
|
+
tuiBriefing: (v, note) => `📋 本次更新 ${v}:${note}`,
|
|
117
120
|
tuiAutoUpdateFailed: (why) => `⤴ 自动更新未能开始(${why}),下次再试;或手动: npm i -g @hmharness/cli@latest`,
|
|
118
121
|
tuiEvolveDone: (p, i, n) => `evolve 完成: ${p} 提案 · 洞察 ${i} · 记忆 ${n}`,
|
|
119
122
|
tuiPassRate: (pct) => `pass rate: ${pct}`,
|
|
@@ -267,6 +270,9 @@ const en = {
|
|
|
267
270
|
opsHint: 'more: /ops scan pull upstream + write today brief · /ops brief latest brief',
|
|
268
271
|
tuiLongOutputHint: (tool, lines) => `⎿ ${tool} produced ${lines} lines (folded) · z expands the last cell · Ctrl+T full transcript`,
|
|
269
272
|
tuiUserFolded: (hidden) => `(…${hidden} more lines folded · Ctrl+T full transcript)`,
|
|
273
|
+
tuiUpdating: (v) => `⤴ new version ${v} - updating in the background (this session is unaffected; effective on next launch)`,
|
|
274
|
+
tuiUpdated: (v) => `✓ updated to ${v} · restart hmh to activate`,
|
|
275
|
+
tuiBriefing: (v, note) => `📋 updated ${v}: ${note}`,
|
|
270
276
|
tuiAutoUpdateFailed: (why) => `⤴ auto-update could not start (${why}); or manually: npm i -g @hmharness/cli@latest`,
|
|
271
277
|
tuiEvolveDone: (p, i, n) => `evolve done: ${p} proposals · ${i} insights · ${n} notes`,
|
|
272
278
|
tuiPassRate: (pct) => `pass rate: ${pct}`,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export { baseTools, readFileTool, writeFileTool, listDirTool, runCommandTool, rememberTool, seeImageTool } from './tools.
|
|
2
|
-
export { manifestFor, capabilityReport, authorize, type CapabilityManifest, type CapabilityRisk, type PolicyMode } from './capability.
|
|
3
|
-
export { checkpointProject, createProject, findProject, interruptProject, listProjects, loadProject, projectFor, releaseProject, restoreCheckpoint, resumeBundle, transitionProject, attachRun, newProjectId, type CheckpointRef, type DecisionEntry, type ProjectRecord, type ProjectState, } from './project.
|
|
4
|
-
export { mechanicalGate, parseVerdict, runPipeline, type PipelineOptions, type PipelineReport, type StageRecord, type StageRole, } from './pipeline.
|
|
5
|
-
export { buildSystemPrompt } from './prompt.
|
|
6
|
-
export { strings, type Locale, type Strings } from './i18n.
|
|
7
|
-
export { makeSpawnTool, MAX_SPAWN_DEPTH, type SpawnBase } from './spawn.
|
|
8
|
-
export { withTopologyGovernance, mapRole, liveTopologySnapshot, teamLog } from './topology-live.
|
|
9
|
-
export { buildRegistry, contextPack, makeApproval, liveYolo, nativeRegistry, runAgentTask, spawnBase, toServerConfig, type AgentTaskOptions, type RunnerEvents, } from './runner.
|
|
10
|
-
export * from './skills-conformance.
|
|
11
|
-
export * from './acp.
|
|
12
|
-
export * from './topology.
|
|
13
|
-
export * from './meta-harness.
|
|
1
|
+
export { baseTools, readFileTool, writeFileTool, listDirTool, runCommandTool, rememberTool, seeImageTool } from './tools.js';
|
|
2
|
+
export { manifestFor, capabilityReport, authorize, type CapabilityManifest, type CapabilityRisk, type PolicyMode } from './capability.js';
|
|
3
|
+
export { checkpointProject, createProject, findProject, interruptProject, listProjects, loadProject, projectFor, releaseProject, restoreCheckpoint, resumeBundle, transitionProject, attachRun, newProjectId, type CheckpointRef, type DecisionEntry, type ProjectRecord, type ProjectState, } from './project.js';
|
|
4
|
+
export { mechanicalGate, parseVerdict, runPipeline, type PipelineOptions, type PipelineReport, type StageRecord, type StageRole, } from './pipeline.js';
|
|
5
|
+
export { buildSystemPrompt } from './prompt.js';
|
|
6
|
+
export { strings, type Locale, type Strings } from './i18n.js';
|
|
7
|
+
export { makeSpawnTool, MAX_SPAWN_DEPTH, type SpawnBase } from './spawn.js';
|
|
8
|
+
export { withTopologyGovernance, mapRole, liveTopologySnapshot, teamLog } from './topology-live.js';
|
|
9
|
+
export { buildRegistry, contextPack, makeApproval, liveYolo, nativeRegistry, runAgentTask, spawnBase, toServerConfig, type AgentTaskOptions, type RunnerEvents, } from './runner.js';
|
|
10
|
+
export * from './skills-conformance.js';
|
|
11
|
+
export * from './acp.js';
|
|
12
|
+
export * from './topology.js';
|
|
13
|
+
export * from './meta-harness.js';
|
package/dist/runner.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { Registry, type ChatMessage, type DeltaKind, type HmhConfig, type LoopApproval, type LoopResult, type McpClient, type McpServerConfig, type McpServerImport, type ToolContext } from '@hmharness/kernel';
|
|
9
9
|
import { type EmbeddingProvider } from '@hmharness/evolution';
|
|
10
10
|
import * as readline from 'node:readline/promises';
|
|
11
|
-
import { type SpawnBase } from './spawn.
|
|
11
|
+
import { type SpawnBase } from './spawn.js';
|
|
12
12
|
/** Flatten the config.json shape into the runtime discriminated union. */
|
|
13
13
|
export declare function toServerConfig(c: McpServerImport): McpServerConfig;
|
|
14
14
|
/**
|
|
@@ -21,6 +21,7 @@ export declare const spawnBase: {
|
|
|
21
21
|
export declare function nativeRegistry(depth: number, opts?: {
|
|
22
22
|
lsp?: boolean;
|
|
23
23
|
browser?: boolean;
|
|
24
|
+
extension?: boolean;
|
|
24
25
|
workspaceRoot?: string;
|
|
25
26
|
}): Registry;
|
|
26
27
|
export declare function buildRegistry(opts?: {
|
package/dist/runner.js
CHANGED
|
@@ -17,6 +17,8 @@ import { harmonyTools } from '@hmharness/domain-harmony';
|
|
|
17
17
|
import { opsTools } from '@hmharness/domain-ops';
|
|
18
18
|
import { discoverServers, lspTools } from '@hmharness/lsp';
|
|
19
19
|
import { discoverBrowsers, browserTools } from '@hmharness/browser';
|
|
20
|
+
import { discoverExtensionBridgeSync, extensionTools } from '@hmharness/extension';
|
|
21
|
+
import { pageReadSink } from "./code-wm-recorder.js";
|
|
20
22
|
import * as readline from 'node:readline/promises';
|
|
21
23
|
import { stdin } from 'node:process';
|
|
22
24
|
import { baseTools } from "./tools.js";
|
|
@@ -65,6 +67,24 @@ export function nativeRegistry(depth, opts = {}) {
|
|
|
65
67
|
}
|
|
66
68
|
catch { /* browser optional: never block tool assembly */ }
|
|
67
69
|
}
|
|
70
|
+
// extension_* family (2026-10-04, ChatGPT-style browser-extension bridge)
|
|
71
|
+
// — the user's REAL browser via the paired extension. Same dead-tools
|
|
72
|
+
// rule, but liveness is a STATE FILE the bridge keeps fresh (registry
|
|
73
|
+
// build stays sync); registration additionally requires an extension
|
|
74
|
+
// to be CONNECTED right now, and the tools themselves re-check the
|
|
75
|
+
// bridge over HTTP on every call.
|
|
76
|
+
if (opts.extension !== false) {
|
|
77
|
+
try {
|
|
78
|
+
const bridge = discoverExtensionBridgeSync(homeDir());
|
|
79
|
+
if (bridge.healthy && bridge.connected) {
|
|
80
|
+
// onPageRead feeds the PERSISTED Code World Model (round 38):
|
|
81
|
+
// every real-browser page observation becomes durable RuntimeFact
|
|
82
|
+
// evidence — best-effort, never able to fail the tool itself.
|
|
83
|
+
reg.registerAll(extensionTools({ home: homeDir(), onPageRead: pageReadSink(homeDir()) }));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch { /* extension optional: never block tool assembly */ }
|
|
87
|
+
}
|
|
68
88
|
if (depth < MAX_SPAWN_DEPTH) {
|
|
69
89
|
// blueprint M10: every spawn_agent is governed by the live topology
|
|
70
90
|
// (role contract + shared budget + immutable multi-agent.jsonl audit);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/agent",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.24",
|
|
4
4
|
"description": "hmharness agent execution layer: base tools, system prompt, sub-agent spawn, and the shared task runner that frontends (cli, web) drive.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
"build": "tsc -p tsconfig.build.json"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@hmharness/cognitive": "0.23.
|
|
19
|
-
"@hmharness/domain-harmony": "0.23.
|
|
20
|
-
"@hmharness/domain-ops": "0.23.
|
|
21
|
-
"@hmharness/evolution": "0.23.
|
|
22
|
-
"@hmharness/kernel": "0.23.
|
|
23
|
-
"@hmharness/observability": "0.23.
|
|
24
|
-
"@hmharness/sandbox": "0.23.
|
|
25
|
-
"@hmharness/lsp": "0.23.
|
|
26
|
-
"@hmharness/browser": "0.23.
|
|
18
|
+
"@hmharness/cognitive": "0.23.24",
|
|
19
|
+
"@hmharness/domain-harmony": "0.23.24",
|
|
20
|
+
"@hmharness/domain-ops": "0.23.24",
|
|
21
|
+
"@hmharness/evolution": "0.23.24",
|
|
22
|
+
"@hmharness/kernel": "0.23.24",
|
|
23
|
+
"@hmharness/observability": "0.23.24",
|
|
24
|
+
"@hmharness/sandbox": "0.23.24",
|
|
25
|
+
"@hmharness/lsp": "0.23.24",
|
|
26
|
+
"@hmharness/browser": "0.23.24",
|
|
27
|
+
"@hmharness/extension": "0.23.24"
|
|
27
28
|
},
|
|
28
29
|
"files": [
|
|
29
30
|
"dist"
|