harnery 0.46.1 → 0.47.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/README.md +24 -0
- package/bin/README.md +10 -0
- package/bin/agent-coord.mjs +3 -0
- package/bin/agent-hook.mjs +3 -0
- package/bin/harn.mjs +3 -0
- package/bin/run.mjs +43 -0
- package/dist/commander.d.ts +18 -6
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +74 -2
- package/dist/commands/agents.d.ts +25 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +70 -27
- package/dist/commands/autoupdate.d.ts +12 -0
- package/dist/commands/autoupdate.d.ts.map +1 -0
- package/dist/commands/autoupdate.js +190 -0
- package/dist/commands/computer.d.ts +45 -0
- package/dist/commands/computer.d.ts.map +1 -0
- package/dist/commands/computer.js +509 -0
- package/dist/commands/docs.js +3 -1
- package/dist/commands/fetch.js +11 -1
- package/dist/commands/grep.d.ts +72 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +200 -47
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +15 -9
- package/dist/commands/rm.js +2 -2
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +2 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -3
- package/dist/core/agents/coord-bin.d.ts +3 -0
- package/dist/core/agents/coord-bin.d.ts.map +1 -1
- package/dist/core/agents/coord-bin.js +6 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +6 -2
- package/dist/core/agents/render/prompt-context.d.ts +55 -7
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +122 -48
- package/dist/core/agents/state/proc-start.d.ts +3 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +26 -4
- package/dist/core/autoupdate/engine.d.ts +97 -0
- package/dist/core/autoupdate/engine.d.ts.map +1 -0
- package/dist/core/autoupdate/engine.js +548 -0
- package/dist/core/autoupdate/fingerprint.d.ts +16 -0
- package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
- package/dist/core/autoupdate/fingerprint.js +73 -0
- package/dist/core/autoupdate/git.d.ts +30 -0
- package/dist/core/autoupdate/git.d.ts.map +1 -0
- package/dist/core/autoupdate/git.js +82 -0
- package/dist/core/autoupdate/index.d.ts +6 -0
- package/dist/core/autoupdate/index.d.ts.map +1 -0
- package/dist/core/autoupdate/index.js +4 -0
- package/dist/core/autoupdate/state.d.ts +59 -0
- package/dist/core/autoupdate/state.d.ts.map +1 -0
- package/dist/core/autoupdate/state.js +214 -0
- package/dist/core/autoupdate/types.d.ts +113 -0
- package/dist/core/autoupdate/types.d.ts.map +1 -0
- package/dist/core/autoupdate/types.js +35 -0
- package/dist/core/config.d.ts +53 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +114 -1
- package/dist/core/events/v3/bootstrap.js +1 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +1 -0
- package/dist/core/hooks/adapter/wiring.d.ts +12 -2
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +38 -8
- package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
- package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
- package/dist/core/hooks/autoupdate-cue.js +53 -0
- package/dist/core/hooks/cli.js +28 -3
- package/dist/core/hooks/effects/index.js +4 -4
- package/dist/core/hooks/resolve/coord-root.js +1 -0
- package/dist/core/semantic/contract.d.ts +3 -3
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +4 -2
- package/dist/core/storage/modes.d.ts +1 -1
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +5 -1
- package/dist/core/workflow/durable-record.d.ts +2 -0
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +13 -1
- package/dist/core/workflow/engine.js +4 -0
- package/dist/core/workflow/spawn-claude.d.ts +7 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +16 -4
- package/dist/core/workflow/specialists.d.ts.map +1 -1
- package/dist/core/workflow/specialists.js +1 -0
- package/dist/core/workflow/types.d.ts +10 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -9
- package/dist/lib/computer/cua.d.ts +50 -0
- package/dist/lib/computer/cua.d.ts.map +1 -0
- package/dist/lib/computer/cua.js +474 -0
- package/dist/lib/computer/host.d.ts +19 -0
- package/dist/lib/computer/host.d.ts.map +1 -0
- package/dist/lib/computer/host.js +72 -0
- package/dist/lib/computer/lease.d.ts +13 -0
- package/dist/lib/computer/lease.d.ts.map +1 -0
- package/dist/lib/computer/lease.js +269 -0
- package/dist/lib/computer/types.d.ts +163 -0
- package/dist/lib/computer/types.d.ts.map +1 -0
- package/dist/lib/computer/types.js +10 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
- package/dist/lib/docs-metadata-v2.js +6 -5
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +1 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +5 -4
- package/dist/lib/headless/index.d.ts +33 -0
- package/dist/lib/headless/index.d.ts.map +1 -1
- package/dist/lib/headless/index.js +38 -2
- package/dist/lib/http/client.d.ts +2 -9
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +30 -9
- package/dist/lib/instructions/apply.d.ts +9 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +92 -14
- package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
- package/dist/lib/instructions/git-hooks.js +8 -4
- package/dist/lib/instructions/templates.d.ts +11 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +81 -1
- package/package.json +5 -5
- package/schemas/config.schema.json +89 -1
- package/src/commander.ts +98 -8
- package/src/commands/agents.ts +100 -32
- package/src/commands/autoupdate.ts +250 -0
- package/src/commands/computer.ts +682 -0
- package/src/commands/docs.ts +3 -1
- package/src/commands/fetch.ts +11 -1
- package/src/commands/grep.ts +274 -59
- package/src/commands/init.ts +17 -7
- package/src/commands/rm.ts +2 -2
- package/src/commands/web.ts +2 -0
- package/src/core/agents/canonical-emit.ts +2 -3
- package/src/core/agents/coord-bin.ts +30 -0
- package/src/core/agents/coord-client.ts +6 -2
- package/src/core/agents/render/prompt-context.ts +152 -53
- package/src/core/agents/state/proc-start.ts +29 -5
- package/src/core/autoupdate/engine.ts +691 -0
- package/src/core/autoupdate/fingerprint.ts +89 -0
- package/src/core/autoupdate/git.ts +120 -0
- package/src/core/autoupdate/index.ts +29 -0
- package/src/core/autoupdate/state.ts +269 -0
- package/src/core/autoupdate/types.ts +135 -0
- package/src/core/config.ts +142 -1
- package/src/core/events/v3/bootstrap.ts +1 -0
- package/src/core/events/v3/runtime-build.ts +1 -0
- package/src/core/hooks/adapter/wiring.ts +51 -6
- package/src/core/hooks/autoupdate-cue.ts +72 -0
- package/src/core/hooks/cli.ts +27 -1
- package/src/core/hooks/effects/index.ts +4 -4
- package/src/core/hooks/resolve/coord-root.ts +1 -0
- package/src/core/semantic/service.ts +6 -2
- package/src/core/storage/modes.ts +7 -1
- package/src/core/workflow/durable-record.ts +15 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/spawn-claude.ts +16 -4
- package/src/core/workflow/specialists.ts +1 -0
- package/src/core/workflow/types.ts +10 -0
- package/src/core/workflow/workspaces/leases.ts +1 -9
- package/src/lib/computer/cua.ts +572 -0
- package/src/lib/computer/host.ts +87 -0
- package/src/lib/computer/lease.ts +299 -0
- package/src/lib/computer/types.ts +177 -0
- package/src/lib/docs-metadata-v2.ts +24 -5
- package/src/lib/exec.ts +1 -0
- package/src/lib/guarded-remove.ts +5 -3
- package/src/lib/headless/index.ts +66 -2
- package/src/lib/http/client.ts +33 -19
- package/src/lib/instructions/apply.ts +100 -12
- package/src/lib/instructions/git-hooks.ts +8 -4
- package/src/lib/instructions/templates.ts +95 -1
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* and forwards the result as the adapter-shaped additionalContext payload.
|
|
8
8
|
*
|
|
9
9
|
* Four subsections combined into one additionalContext payload:
|
|
10
|
-
* 1. Peer table:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 1. Peer table: re-emits only when a live peer arrives or leaves, changes
|
|
11
|
+
* lifecycle, or changes its claimed files. Activity and task text never
|
|
12
|
+
* trigger a reprint. Peers past the heartbeat-freshness window are left
|
|
13
|
+
* out and counted in "+N more".
|
|
13
14
|
* 2. Council pending: pending open-council IDs hashed; re-emits when the
|
|
14
15
|
* ID set changes.
|
|
15
16
|
* 3. Focus nudge. Until the session has displayed its current suggested
|
|
@@ -24,14 +25,37 @@
|
|
|
24
25
|
* 5. Codex status footer: a non-deduplicated reminder to put the live status
|
|
25
26
|
* box after the substantive answer. Stop stays observe-only, so missing the
|
|
26
27
|
* footer can never trigger a replacement response.
|
|
28
|
+
* 6. Turn ritual: for adapters whose Stop hook enforces the ritual, shown on
|
|
29
|
+
* the first prompt, after a compaction, and after a bounced turn.
|
|
27
30
|
*
|
|
28
31
|
* Hash files live at:
|
|
32
|
+
* .harnery/.last-turn-ritual.<instance_id>
|
|
29
33
|
* .harnery/.last-peer-hash.<instance_id>
|
|
30
34
|
* .harnery/.last-council-hash.<instance_id>
|
|
31
35
|
* .harnery/.last-task-nudge-hash.<instance_id>
|
|
32
36
|
*
|
|
33
37
|
* First call always emits (hash files don't exist).
|
|
34
38
|
*/
|
|
39
|
+
import { type RemoteMachine } from "../../presence/index.js";
|
|
40
|
+
import type { AgentActivity, TaskState } from "../state/session-state.js";
|
|
41
|
+
interface HeartbeatRow {
|
|
42
|
+
instance_id?: string;
|
|
43
|
+
name?: string;
|
|
44
|
+
kind?: string;
|
|
45
|
+
session_id?: string;
|
|
46
|
+
started_at?: string;
|
|
47
|
+
last_heartbeat?: string;
|
|
48
|
+
files_touched?: string[];
|
|
49
|
+
platform?: string;
|
|
50
|
+
task?: string;
|
|
51
|
+
activity?: AgentActivity;
|
|
52
|
+
task_state?: TaskState;
|
|
53
|
+
task_state_reason?: string;
|
|
54
|
+
task_updated_at?: string | null;
|
|
55
|
+
suggested_session_name?: string;
|
|
56
|
+
session_name_seen_for?: string;
|
|
57
|
+
workflow_run_id?: string;
|
|
58
|
+
}
|
|
35
59
|
export interface PromptContextOpts {
|
|
36
60
|
coordRoot: string;
|
|
37
61
|
instanceId: string;
|
|
@@ -50,10 +74,9 @@ export interface PromptContextOpts {
|
|
|
50
74
|
* The caller enables this only for human-facing Codex sessions. */
|
|
51
75
|
statusFooterNudge?: boolean;
|
|
52
76
|
/** Adapter id whose Stop hook ENFORCES the end-of-turn ritual (task_set +
|
|
53
|
-
* status check). When set, append a
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* status footer. */
|
|
77
|
+
* status check). When set, append a reminder of that contract on the
|
|
78
|
+
* session's first prompt, after a context compaction, and after a turn the
|
|
79
|
+
* Stop hook bounced (see `markTurnRitualBounced`). */
|
|
57
80
|
turnRitualNudge?: string;
|
|
58
81
|
/** The emitter acknowledges exactly these IDs only after output succeeds. */
|
|
59
82
|
onInboxPrepared?: (messageIds: readonly string[]) => void;
|
|
@@ -74,4 +97,29 @@ export declare function renderPromptContext(opts: PromptContextOpts): string;
|
|
|
74
97
|
* re-emit the same advice.
|
|
75
98
|
*/
|
|
76
99
|
export declare function resourceWarningIfChanged(coordRoot: string, instanceId: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Called by the Stop hook when it blocks a turn for a missing ritual step, so
|
|
102
|
+
* the next prompt carries the reminder again.
|
|
103
|
+
*/
|
|
104
|
+
export declare function markTurnRitualBounced(coordRoot: string, instanceId: string): void;
|
|
105
|
+
export interface PeerTableInput {
|
|
106
|
+
peers: HeartbeatRow[];
|
|
107
|
+
remote: RemoteMachine[];
|
|
108
|
+
mySessionId: string;
|
|
109
|
+
nowMs: number;
|
|
110
|
+
/** Sessions whose last heartbeat is older than this are folded into "+N more". */
|
|
111
|
+
freshnessSeconds: number;
|
|
112
|
+
relations?: Map<string, string>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Pure peer-table render plus its change key. The key covers only what makes
|
|
116
|
+
* the table news: which live sessions exist, their lifecycle, and the files
|
|
117
|
+
* they claim. Activity and task text change on nearly every peer tool call, so
|
|
118
|
+
* they render in the table but never trigger a reprint by themselves.
|
|
119
|
+
*/
|
|
120
|
+
export declare function renderPeerTable(input: PeerTableInput): {
|
|
121
|
+
hash: string;
|
|
122
|
+
table: string;
|
|
123
|
+
};
|
|
124
|
+
export {};
|
|
77
125
|
//# sourceMappingURL=prompt-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/render/prompt-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAwBH,OAAO,EAAE,KAAK,aAAa,EAAsB,MAAM,yBAAyB,CAAC;AASjF,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAG1E,UAAU,YAAY;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,UAAU,CAAC,EAAE,SAAS,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;8EAC0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;kFAC8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;kFAC8E;IAC9E,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;uEACmE;IACnE,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;0DAGsD;IACtD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CA6EnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CA6CtF;AAsBD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAEjF;AA0LD,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA2CtF"}
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* and forwards the result as the adapter-shaped additionalContext payload.
|
|
8
8
|
*
|
|
9
9
|
* Four subsections combined into one additionalContext payload:
|
|
10
|
-
* 1. Peer table:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 1. Peer table: re-emits only when a live peer arrives or leaves, changes
|
|
11
|
+
* lifecycle, or changes its claimed files. Activity and task text never
|
|
12
|
+
* trigger a reprint. Peers past the heartbeat-freshness window are left
|
|
13
|
+
* out and counted in "+N more".
|
|
13
14
|
* 2. Council pending: pending open-council IDs hashed; re-emits when the
|
|
14
15
|
* ID set changes.
|
|
15
16
|
* 3. Focus nudge. Until the session has displayed its current suggested
|
|
@@ -24,8 +25,11 @@
|
|
|
24
25
|
* 5. Codex status footer: a non-deduplicated reminder to put the live status
|
|
25
26
|
* box after the substantive answer. Stop stays observe-only, so missing the
|
|
26
27
|
* footer can never trigger a replacement response.
|
|
28
|
+
* 6. Turn ritual: for adapters whose Stop hook enforces the ritual, shown on
|
|
29
|
+
* the first prompt, after a compaction, and after a bounced turn.
|
|
27
30
|
*
|
|
28
31
|
* Hash files live at:
|
|
32
|
+
* .harnery/.last-turn-ritual.<instance_id>
|
|
29
33
|
* .harnery/.last-peer-hash.<instance_id>
|
|
30
34
|
* .harnery/.last-council-hash.<instance_id>
|
|
31
35
|
* .harnery/.last-task-nudge-hash.<instance_id>
|
|
@@ -36,7 +40,8 @@ import { createHash } from "node:crypto";
|
|
|
36
40
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
37
41
|
import { join } from "node:path";
|
|
38
42
|
import { coordEnv } from "../../../lib/env.js";
|
|
39
|
-
import { endOfTurnStatusCommand, hostPromptReminder, resolveBinName } from "../../config.js";
|
|
43
|
+
import { coordFreshnessSeconds, endOfTurnStatusCommand, hostPromptReminder, resolveBinName, } from "../../config.js";
|
|
44
|
+
import { readContextState } from "../../context/index.js";
|
|
40
45
|
import { formatPeerInbox, peerInbox } from "../../inbox/peer-messages.js";
|
|
41
46
|
import { readRemoteMachines } from "../../presence/index.js";
|
|
42
47
|
import { readResourceStatus } from "../../resources/status.js";
|
|
@@ -107,13 +112,12 @@ export function renderPromptContext(opts) {
|
|
|
107
112
|
if (statusMsg)
|
|
108
113
|
sections.push(statusMsg);
|
|
109
114
|
}
|
|
110
|
-
// 7. Adapters whose Stop hook ENFORCES the turn ritual get
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// hash-deduped for the same reason as the Codex footer.
|
|
115
|
+
// 7. Adapters whose Stop hook ENFORCES the turn ritual get the reminder when
|
|
116
|
+
// it is news: on the session's first prompt, after a context compaction,
|
|
117
|
+
// and after a turn the Stop hook bounced. The Stop hook keeps enforcing on
|
|
118
|
+
// every turn, so a missed ritual still costs one retry and re-arms this.
|
|
115
119
|
if (turnRitualNudge) {
|
|
116
|
-
const ritualMsg = renderTurnRitualReminder(coordRoot, instanceId, turnRitualNudge);
|
|
120
|
+
const ritualMsg = renderTurnRitualReminder(coordRoot, instanceId, sessionId, turnRitualNudge);
|
|
117
121
|
if (ritualMsg)
|
|
118
122
|
sections.push(ritualMsg);
|
|
119
123
|
}
|
|
@@ -181,17 +185,46 @@ function renderHostPromptReminder(coordRoot, selfInstanceId) {
|
|
|
181
185
|
return "";
|
|
182
186
|
return hostPromptReminder(coordRoot) ?? "";
|
|
183
187
|
}
|
|
184
|
-
function
|
|
188
|
+
function turnRitualStatePath(coordRoot, instanceId) {
|
|
189
|
+
return join(coordRoot, ".harnery", `.last-turn-ritual.${instanceId}`);
|
|
190
|
+
}
|
|
191
|
+
const TURN_RITUAL_BOUNCED = "bounced";
|
|
192
|
+
/**
|
|
193
|
+
* Called by the Stop hook when it blocks a turn for a missing ritual step, so
|
|
194
|
+
* the next prompt carries the reminder again.
|
|
195
|
+
*/
|
|
196
|
+
export function markTurnRitualBounced(coordRoot, instanceId) {
|
|
197
|
+
writeHashFile(turnRitualStatePath(coordRoot, instanceId), TURN_RITUAL_BOUNCED);
|
|
198
|
+
}
|
|
199
|
+
function renderTurnRitualReminder(coordRoot, selfInstanceId, sessionId, adapter) {
|
|
185
200
|
const hb = readLiveCoordinationRow(coordRoot, selfInstanceId);
|
|
186
201
|
if (!hb || hb.kind === "subagent" || hb.kind === "transient" || hb.workflow_run_id)
|
|
187
202
|
return "";
|
|
203
|
+
// The key changes when native context compaction drops the earlier reminder.
|
|
204
|
+
let generation = 0;
|
|
205
|
+
try {
|
|
206
|
+
generation = readContextState(coordRoot, sessionId)?.generation ?? 0;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
/* no continuity state: treat as the first context generation */
|
|
210
|
+
}
|
|
211
|
+
const key = `context-generation=${generation}`;
|
|
212
|
+
const statePath = turnRitualStatePath(coordRoot, selfInstanceId);
|
|
213
|
+
const previous = safeRead(statePath);
|
|
214
|
+
if (previous === key)
|
|
215
|
+
return "";
|
|
216
|
+
writeHashFile(statePath, key);
|
|
217
|
+
const bounced = previous === TURN_RITUAL_BOUNCED
|
|
218
|
+
? "Your previous turn was bounced by the Stop hook because the ritual was incomplete. "
|
|
219
|
+
: "";
|
|
188
220
|
const bin = resolveBinName(coordRoot);
|
|
189
221
|
const statusCommand = endOfTurnStatusCommand(coordRoot);
|
|
190
222
|
// Claude Code and Cursor can both enforce completed assistant reply text.
|
|
191
223
|
const paste = adapter === "claude-code" || adapter === "cursor"
|
|
192
224
|
? " and paste its output verbatim in a fenced code block at the end of your reply"
|
|
193
225
|
: "";
|
|
194
|
-
return (
|
|
226
|
+
return (bounced +
|
|
227
|
+
`Turn ritual (Stop-enforced): on any turn that uses tools, run \`${bin} agents set-task "<short focus>"\` at some point during the turn (required every turn, even when the focus is unchanged; pass an empty string when there is no meaningful focus), then make \`${statusCommand}\` your final tool call${paste}. ` +
|
|
195
228
|
"A turn that ends without these is bounced by the Stop hook and costs a retry.");
|
|
196
229
|
}
|
|
197
230
|
function renderStatusFooterReminder(coordRoot, selfInstanceId) {
|
|
@@ -299,8 +332,43 @@ function computePeerTableIfChanged(coordRoot, selfInstanceId, selfSessionIdFallb
|
|
|
299
332
|
const remote = readRemoteMachinesSafe(coordRoot);
|
|
300
333
|
if (peers.length === 0 && remote.length === 0)
|
|
301
334
|
return "";
|
|
302
|
-
|
|
303
|
-
|
|
335
|
+
const rendered = renderPeerTable({
|
|
336
|
+
peers,
|
|
337
|
+
remote,
|
|
338
|
+
mySessionId,
|
|
339
|
+
nowMs: Date.now(),
|
|
340
|
+
freshnessSeconds: coordFreshnessSeconds(coordRoot),
|
|
341
|
+
relations: forkRelationLabels(coordRoot, selfInstanceId, new Set(peers.map((p) => p.instance_id ?? ""))),
|
|
342
|
+
});
|
|
343
|
+
if (!rendered.table)
|
|
344
|
+
return "";
|
|
345
|
+
const hashFile = join(coordRoot, ".harnery", `.last-peer-hash.${selfInstanceId}`);
|
|
346
|
+
const oldHash = safeRead(hashFile);
|
|
347
|
+
if (oldHash && oldHash === rendered.hash)
|
|
348
|
+
return "";
|
|
349
|
+
// Persist hash atomically (temp + rename, same convention as other coord writes).
|
|
350
|
+
writeHashFile(hashFile, rendered.hash);
|
|
351
|
+
return rendered.table;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Pure peer-table render plus its change key. The key covers only what makes
|
|
355
|
+
* the table news: which live sessions exist, their lifecycle, and the files
|
|
356
|
+
* they claim. Activity and task text change on nearly every peer tool call, so
|
|
357
|
+
* they render in the table but never trigger a reprint by themselves.
|
|
358
|
+
*/
|
|
359
|
+
export function renderPeerTable(input) {
|
|
360
|
+
const { peers, remote, mySessionId, nowMs, freshnessSeconds } = input;
|
|
361
|
+
const cutoffMs = nowMs - freshnessSeconds * 1000;
|
|
362
|
+
const live = [];
|
|
363
|
+
const stale = [];
|
|
364
|
+
for (const peer of peers) {
|
|
365
|
+
const seenMs = Date.parse(peer.last_heartbeat ?? "");
|
|
366
|
+
if (Number.isFinite(seenMs) && seenMs >= cutoffMs)
|
|
367
|
+
live.push(peer);
|
|
368
|
+
else
|
|
369
|
+
stale.push(peer);
|
|
370
|
+
}
|
|
371
|
+
const basis = live
|
|
304
372
|
.map((p) => ({
|
|
305
373
|
name: p.name ?? null,
|
|
306
374
|
instance_id: p.instance_id ?? null,
|
|
@@ -309,39 +377,28 @@ function computePeerTableIfChanged(coordRoot, selfInstanceId, selfSessionIdFallb
|
|
|
309
377
|
started_at: p.started_at ?? null,
|
|
310
378
|
files_touched: Array.from(p.files_touched ?? []).sort(),
|
|
311
379
|
platform: p.platform ?? null,
|
|
312
|
-
activity: p.activity ?? "unknown",
|
|
313
380
|
task_state: p.task_state ?? "active",
|
|
314
381
|
task_state_reason: p.task_state_reason ?? null,
|
|
315
382
|
}))
|
|
316
383
|
.sort((a, b) => (a.instance_id ?? "").localeCompare(b.instance_id ?? ""));
|
|
317
|
-
// Remote basis: machine + per-agent identity
|
|
318
|
-
//
|
|
384
|
+
// Remote basis: machine + per-agent identity, lifecycle, and files
|
|
385
|
+
// (published_at, activity, and task are excluded for the same reason).
|
|
319
386
|
const remoteBasis = remote.map((m) => ({
|
|
320
387
|
machine: m.machine,
|
|
321
388
|
agents: m.agents.map((a) => ({
|
|
322
389
|
instance_id: a.instance_id,
|
|
323
390
|
name: a.name ?? null,
|
|
324
|
-
task: a.task ?? null,
|
|
325
|
-
activity: a.activity,
|
|
326
391
|
task_state: a.task_state,
|
|
327
392
|
task_state_reason: a.task_state_reason ?? null,
|
|
328
393
|
files_touched: Array.from(a.files_touched ?? []).sort(),
|
|
329
394
|
})),
|
|
330
395
|
}));
|
|
331
|
-
const
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
return "";
|
|
336
|
-
// Render peer table via the same formatter used at SessionStart.
|
|
337
|
-
const local = formatPeerTable(peers, mySessionId, forkRelationLabels(coordRoot, selfInstanceId, new Set(peers.map((p) => p.instance_id ?? ""))));
|
|
338
|
-
const remoteTable = formatRemoteTable(remote);
|
|
396
|
+
const hash = sha256Hex16(JSON.stringify({ local: basis, remote: remoteBasis }));
|
|
397
|
+
const nowSec = Math.floor(nowMs / 1000);
|
|
398
|
+
const local = formatPeerTable(live, stale, mySessionId, nowSec, input.relations);
|
|
399
|
+
const remoteTable = formatRemoteTable(remote, nowSec);
|
|
339
400
|
const table = [local, remoteTable].filter(Boolean).join("\n\n");
|
|
340
|
-
|
|
341
|
-
return "";
|
|
342
|
-
// Persist hash atomically (temp + rename, same convention as other coord writes).
|
|
343
|
-
writeHashFile(hashFile, newHash);
|
|
344
|
-
return table;
|
|
401
|
+
return { hash, table };
|
|
345
402
|
}
|
|
346
403
|
/** readRemoteMachines with a local failure guard (rendering must never throw). */
|
|
347
404
|
function readRemoteMachinesSafe(coordRoot) {
|
|
@@ -353,10 +410,9 @@ function readRemoteMachinesSafe(coordRoot) {
|
|
|
353
410
|
}
|
|
354
411
|
}
|
|
355
412
|
/** Render sessions on other machines (presence transport) as a subtable. */
|
|
356
|
-
function formatRemoteTable(remote) {
|
|
413
|
+
function formatRemoteTable(remote, nowSec) {
|
|
357
414
|
if (remote.length === 0)
|
|
358
415
|
return "";
|
|
359
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
360
416
|
const lines = [];
|
|
361
417
|
for (const m of remote) {
|
|
362
418
|
for (const a of m.agents.slice(0, 10)) {
|
|
@@ -451,16 +507,11 @@ function writeHashFile(path, value) {
|
|
|
451
507
|
/* swallow */
|
|
452
508
|
}
|
|
453
509
|
}
|
|
454
|
-
|
|
455
|
-
* intentionally co-located here so the prompt-context renderer is self-contained
|
|
456
|
-
* (no cross-file imports of formatting internals). If both renderers diverge,
|
|
457
|
-
* pull the shared bits into a small util module. */
|
|
458
|
-
function formatPeerTable(peers, mySessionId, relations = new Map()) {
|
|
510
|
+
function formatPeerTable(peers, stalePeers, mySessionId, nowSec, relations = new Map()) {
|
|
459
511
|
if (peers.length === 0)
|
|
460
512
|
return "";
|
|
461
|
-
const nowSec = Math.floor(Date.now() / 1000);
|
|
462
513
|
const fold = {};
|
|
463
|
-
for (const p of peers) {
|
|
514
|
+
for (const p of [...peers, ...stalePeers]) {
|
|
464
515
|
const kind = p.kind ?? "unknown";
|
|
465
516
|
if (kind === "transient" && p.session_id) {
|
|
466
517
|
fold[p.session_id] = (fold[p.session_id] ?? []).concat(p.files_touched ?? []);
|
|
@@ -474,28 +525,52 @@ function formatPeerTable(peers, mySessionId, relations = new Map()) {
|
|
|
474
525
|
const relation = relations.get(p.instance_id ?? "");
|
|
475
526
|
return { ...p, display_files: display, ...(relation ? { relation } : {}) };
|
|
476
527
|
});
|
|
528
|
+
assignUniqueLabels(rows);
|
|
529
|
+
const staleRows = stalePeers.filter((p) => (p.kind ?? "unknown") !== "transient");
|
|
477
530
|
// Related sessions sort first so a fork parent never hides in "+N more".
|
|
478
531
|
const blocking = rows
|
|
479
532
|
.filter((p) => p.session_id !== mySessionId)
|
|
480
533
|
.sort((a, b) => Number(!!b.relation) - Number(!!a.relation) || byStartedAt(a, b));
|
|
481
534
|
const group = rows.filter((p) => p.session_id === mySessionId).sort(byStartedAt);
|
|
535
|
+
const staleBlocking = staleRows.filter((p) => p.session_id !== mySessionId).length;
|
|
536
|
+
const staleGroup = staleRows.length - staleBlocking;
|
|
482
537
|
const out = [];
|
|
483
|
-
const blk = renderSubtable(blocking, "Other agent groups active (their files block you):", nowSec);
|
|
538
|
+
const blk = renderSubtable(blocking, staleBlocking, "Other agent groups active (their files block you):", nowSec);
|
|
484
539
|
if (blk)
|
|
485
540
|
out.push(blk);
|
|
486
|
-
const grp = renderSubtable(group, "Your group (subagents / parent / siblings; no mutual block):", nowSec);
|
|
541
|
+
const grp = renderSubtable(group, staleGroup, "Your group (subagents / parent / siblings; no mutual block):", nowSec);
|
|
487
542
|
if (grp)
|
|
488
543
|
out.push(grp);
|
|
489
544
|
return out.join("\n\n");
|
|
490
545
|
}
|
|
546
|
+
/**
|
|
547
|
+
* One full, distinct label per row. An unnamed session shows its whole
|
|
548
|
+
* instance id (an 8-character prefix collides for time-ordered ids), and a
|
|
549
|
+
* name two live rows share carries its instance id so they read as two.
|
|
550
|
+
*/
|
|
551
|
+
function assignUniqueLabels(rows) {
|
|
552
|
+
const base = (r) => r.name ?? r.instance_id ?? "unknown";
|
|
553
|
+
const counts = new Map();
|
|
554
|
+
for (const r of rows)
|
|
555
|
+
counts.set(base(r), (counts.get(base(r)) ?? 0) + 1);
|
|
556
|
+
for (const r of rows) {
|
|
557
|
+
const label = base(r);
|
|
558
|
+
r.label =
|
|
559
|
+
(counts.get(label) ?? 0) > 1 && r.name && r.instance_id
|
|
560
|
+
? `${label} (${r.instance_id})`
|
|
561
|
+
: label;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
491
564
|
function byStartedAt(a, b) {
|
|
492
565
|
return (a.started_at ?? "").localeCompare(b.started_at ?? "");
|
|
493
566
|
}
|
|
494
|
-
function renderSubtable(rows, header, nowSec) {
|
|
567
|
+
function renderSubtable(rows, staleCount, header, nowSec) {
|
|
495
568
|
if (rows.length === 0)
|
|
496
569
|
return "";
|
|
497
570
|
const first = rows.slice(0, 10).map((r) => formatRow(r, nowSec));
|
|
498
|
-
const
|
|
571
|
+
const more = Math.max(0, rows.length - 10) + staleCount;
|
|
572
|
+
const staleNote = staleCount > 0 ? ` (${staleCount} with no recent heartbeat)` : "";
|
|
573
|
+
const overflow = more > 0 ? `\n +${more} more${staleNote}` : "";
|
|
499
574
|
return `${header}\n${first.join("\n")}${overflow}`;
|
|
500
575
|
}
|
|
501
576
|
function formatRow(r, nowSec) {
|
|
@@ -506,9 +581,8 @@ function formatRow(r, nowSec) {
|
|
|
506
581
|
const startedSec = parseIsoSec(r.started_at) ?? parseIsoSec(r.last_heartbeat);
|
|
507
582
|
const ageFrom = startedSec === null ? "age unknown" : fmtAge(nowSec - startedSec);
|
|
508
583
|
const filesPart = fmtFiles(r.display_files);
|
|
509
|
-
//
|
|
510
|
-
|
|
511
|
-
const label = r.name ?? (r.instance_id ? r.instance_id.slice(0, 8) : "unknown");
|
|
584
|
+
// An unnamed row shows its full instance id; a prefix can collide.
|
|
585
|
+
const label = r.label ?? r.name ?? r.instance_id ?? "unknown";
|
|
512
586
|
const relation = r.relation ? ` [${r.relation}]` : "";
|
|
513
587
|
return ` - agent-${label}${relation}${taskPart} (${statePart}, ${ageFrom}, ${filesPart})`;
|
|
514
588
|
}
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* two callers reading the same live process disagree.
|
|
32
32
|
*/
|
|
33
33
|
/** Which probe answers "when did this process start?" on this machine. */
|
|
34
|
-
export type StartTokenProbe = "procfs" | "ps";
|
|
34
|
+
export type StartTokenProbe = "procfs" | "ps" | "windows";
|
|
35
35
|
/**
|
|
36
36
|
* Which probe this machine uses, decided once.
|
|
37
37
|
*
|
|
@@ -68,6 +68,8 @@ export declare function psStartToken(pid: number, run?: (pid: number) => {
|
|
|
68
68
|
* will not say.
|
|
69
69
|
*/
|
|
70
70
|
export declare function processStartToken(pid: number): string | null;
|
|
71
|
+
/** Windows process creation time in invariant UTC ticks, independent of locale. */
|
|
72
|
+
export declare function windowsStartToken(pid: number): string | null;
|
|
71
73
|
export type ProcessGenerationVerdict = "same" | "different" | "unknown";
|
|
72
74
|
type ReadProcessStartToken = (pid: number) => string | null;
|
|
73
75
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"proc-start.d.ts","sourceRoot":"","sources":["../../../../src/core/agents/state/proc-start.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;AAc1D;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,eAAe,CAQjD;AAyBD,uDAAuD;AACvD,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc3D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,GAAG,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAiB,GAC7E,MAAM,GAAG,IAAI,CAUf;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5D;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB5D;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;AAExE,KAAK,qBAAqB,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAE5D;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,SAAS,GAAE,qBAAyC,GACnD,wBAAwB,CAuB1B;AAOD,MAAM,WAAW,mCAAmC;IAClD,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,KAAK,OAAO,CAAC;CAClE;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACxC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,MAAM,EAC/B,YAAY,GAAE,mCAAwC,GACrD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,wBAAwB,CAAA;CAAE,CAUtD"}
|
|
@@ -52,10 +52,11 @@ let cachedBootId;
|
|
|
52
52
|
*/
|
|
53
53
|
export function startTokenProbe() {
|
|
54
54
|
const override = coordEnv("PID_PROBE");
|
|
55
|
-
if (override === "procfs" || override === "ps")
|
|
55
|
+
if (override === "procfs" || override === "ps" || override === "windows")
|
|
56
56
|
return override;
|
|
57
57
|
if (cachedProbe === undefined) {
|
|
58
|
-
cachedProbe =
|
|
58
|
+
cachedProbe =
|
|
59
|
+
process.platform === "win32" ? "windows" : existsSync("/proc/self/stat") ? "procfs" : "ps";
|
|
59
60
|
}
|
|
60
61
|
return cachedProbe;
|
|
61
62
|
}
|
|
@@ -147,9 +148,30 @@ function defaultPsRun(pid) {
|
|
|
147
148
|
* will not say.
|
|
148
149
|
*/
|
|
149
150
|
export function processStartToken(pid) {
|
|
150
|
-
if (!Number.
|
|
151
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
151
152
|
return null;
|
|
152
|
-
|
|
153
|
+
const probe = startTokenProbe();
|
|
154
|
+
if (probe === "windows")
|
|
155
|
+
return windowsStartToken(pid);
|
|
156
|
+
return probe === "procfs" ? procfsStartToken(pid) : psStartToken(pid);
|
|
157
|
+
}
|
|
158
|
+
/** Windows process creation time in invariant UTC ticks, independent of locale. */
|
|
159
|
+
export function windowsStartToken(pid) {
|
|
160
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
161
|
+
return null;
|
|
162
|
+
try {
|
|
163
|
+
const out = spawnSync("powershell.exe", [
|
|
164
|
+
"-NoProfile",
|
|
165
|
+
"-NonInteractive",
|
|
166
|
+
"-Command",
|
|
167
|
+
`try { (Get-Process -Id ${pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks } catch { exit 1 }`,
|
|
168
|
+
], { encoding: "utf8", timeout: 4_000, windowsHide: true });
|
|
169
|
+
const ticks = out.stdout?.trim();
|
|
170
|
+
return out.status === 0 && ticks && /^\d+$/.test(ticks) ? `w${ticks}` : null;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
153
175
|
}
|
|
154
176
|
/**
|
|
155
177
|
* Does `pid` still name the run that wrote `recordedToken`?
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project self-update engine (ADR 0229).
|
|
3
|
+
*
|
|
4
|
+
* `autoUpdateTick` is the hot path every prompt, session start, and host CLI
|
|
5
|
+
* command calls: one stat of the throttle stamp when nothing is due. When the
|
|
6
|
+
* stamp is stale it claims the stamp and launches a detached
|
|
7
|
+
* `<harn> autoupdate run --background`, which decides with `checkForUpdate`
|
|
8
|
+
* and, only when needed, runs the host's configured steps under a lock.
|
|
9
|
+
*/
|
|
10
|
+
import { type GitRunner } from "./git.js";
|
|
11
|
+
import { type LockInspection, type LockOwner } from "./state.js";
|
|
12
|
+
import { type AutoUpdateConfig, type AutoUpdateNotice, type AutoUpdateState, type CheckDecision, type RunOutcome, type RunRecord, type TickResult } from "./types.js";
|
|
13
|
+
/** Set on the detached runner and every step so nested CLI calls never tick. */
|
|
14
|
+
export declare const AUTOUPDATE_CHILD_ENV = "HARNERY_AUTOUPDATE_CHILD";
|
|
15
|
+
export interface StepRunResult {
|
|
16
|
+
exitCode: number | null;
|
|
17
|
+
timedOut: boolean;
|
|
18
|
+
output: string;
|
|
19
|
+
ms: number;
|
|
20
|
+
}
|
|
21
|
+
export type StepRunner = (argv: string[], opts: {
|
|
22
|
+
cwd: string;
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
env: NodeJS.ProcessEnv;
|
|
25
|
+
onChunk?: (chunk: string, stream: "stdout" | "stderr") => void;
|
|
26
|
+
}) => Promise<StepRunResult>;
|
|
27
|
+
export interface AutoUpdateDeps {
|
|
28
|
+
now?: () => number;
|
|
29
|
+
env?: NodeJS.ProcessEnv;
|
|
30
|
+
readConfig?: (coordRoot: string) => AutoUpdateConfig | null;
|
|
31
|
+
git?: GitRunner;
|
|
32
|
+
/** Launch the detached runner (tick). */
|
|
33
|
+
spawnRunner?: (coordRoot: string) => void;
|
|
34
|
+
runStep?: StepRunner;
|
|
35
|
+
/** Expired-artifact cleanup; best effort. */
|
|
36
|
+
runCleanup?: (coordRoot: string) => void;
|
|
37
|
+
binName?: string;
|
|
38
|
+
}
|
|
39
|
+
/** The CLI entry the detached runner executes with `process.execPath`. */
|
|
40
|
+
export declare function harnCliEntry(): string;
|
|
41
|
+
/** Set the throttle stamp's mtime (creating it), which is all the tick reads. */
|
|
42
|
+
export declare function setStamp(coordRoot: string, atMs: number): void;
|
|
43
|
+
/** Keep the log bounded: past 256 KB, keep only its last 64 KB. */
|
|
44
|
+
export declare function rotateLog(path: string): void;
|
|
45
|
+
export declare function isInstructionPath(path: string): boolean;
|
|
46
|
+
export declare function isHookConfigPath(path: string): boolean;
|
|
47
|
+
/** `harnery-notice:` lines in step output. */
|
|
48
|
+
export declare function parseNoticeLines(output: string): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Activity hook. Never throws. When throttled it reads the config and stats
|
|
51
|
+
* one file; it runs no Git and parses no state.
|
|
52
|
+
*/
|
|
53
|
+
export declare function autoUpdateTick(coordRoot: string, deps?: AutoUpdateDeps): TickResult;
|
|
54
|
+
export declare function checkForUpdate(coordRoot: string, cfg: AutoUpdateConfig, state: AutoUpdateState, deps?: AutoUpdateDeps & {
|
|
55
|
+
force?: boolean;
|
|
56
|
+
}): CheckDecision;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a step's argv for the current platform. On Windows a bare command
|
|
59
|
+
* name such as `bun` or `git` is looked up on PATH with PATHEXT, because
|
|
60
|
+
* spawning without a shell does not try `.cmd` shims; a `.cmd` or `.bat`
|
|
61
|
+
* match runs through `cmd.exe`. Elsewhere the argv is used as is.
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveStepCommand(cmd: string, args: string[], env: NodeJS.ProcessEnv, platform?: NodeJS.Platform, exists?: (path: string) => boolean): {
|
|
64
|
+
cmd: string;
|
|
65
|
+
args: string[];
|
|
66
|
+
verbatim: boolean;
|
|
67
|
+
};
|
|
68
|
+
export declare const defaultStepRunner: StepRunner;
|
|
69
|
+
export interface RunAutoUpdateOptions {
|
|
70
|
+
force?: boolean;
|
|
71
|
+
/** Detached runner: output goes to the log only. */
|
|
72
|
+
background?: boolean;
|
|
73
|
+
/** Foreground streaming of step output (in addition to the log). */
|
|
74
|
+
onOutput?: (chunk: string, stream: "stdout" | "stderr") => void;
|
|
75
|
+
deps?: AutoUpdateDeps;
|
|
76
|
+
}
|
|
77
|
+
export interface RunAutoUpdateResult {
|
|
78
|
+
outcome: RunOutcome;
|
|
79
|
+
decision: CheckDecision | null;
|
|
80
|
+
record: RunRecord | null;
|
|
81
|
+
/** Notices recorded by this run. */
|
|
82
|
+
notices: AutoUpdateNotice[];
|
|
83
|
+
/** Holder of the lock when `skipped-locked`. */
|
|
84
|
+
lockOwner?: LockOwner | null;
|
|
85
|
+
}
|
|
86
|
+
export declare function runAutoUpdate(coordRootRaw: string, opts?: RunAutoUpdateOptions): Promise<RunAutoUpdateResult>;
|
|
87
|
+
export interface AutoUpdateStatus {
|
|
88
|
+
config: AutoUpdateConfig | null;
|
|
89
|
+
state: AutoUpdateState;
|
|
90
|
+
/** Milliseconds since the throttle stamp (negative while backing off); null when never checked. */
|
|
91
|
+
stampAge: number | null;
|
|
92
|
+
/** Milliseconds until a tick would launch a check; 0 when due. */
|
|
93
|
+
nextCheckMs: number | null;
|
|
94
|
+
locked: LockInspection;
|
|
95
|
+
}
|
|
96
|
+
export declare function autoUpdateStatus(coordRootRaw: string, deps?: AutoUpdateDeps): AutoUpdateStatus;
|
|
97
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/core/autoupdate/engine.ts"],"names":[],"mappings":"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"}
|