harnery 0.42.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.d.ts +16 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +18 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +54 -4
- package/dist/commands/tunnel.d.ts +65 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +290 -101
- package/dist/commands/web-fetch.d.ts +3 -2
- package/dist/commands/web-fetch.d.ts.map +1 -1
- package/dist/commands/web-fetch.js +25 -19
- package/dist/commands/web.d.ts +7 -1
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +9 -3
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +11 -5
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +57 -13
- package/dist/core/agents/state/names.d.ts +43 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +87 -3
- package/dist/core/events/v3/support-storage/pack-reader.d.ts.map +1 -1
- package/dist/core/events/v3/support-storage/pack-reader.js +42 -16
- package/dist/core/hooks/cli.js +87 -18
- package/dist/core/hooks/fork-lineage.d.ts +40 -0
- package/dist/core/hooks/fork-lineage.d.ts.map +1 -0
- package/dist/core/hooks/fork-lineage.js +62 -0
- package/dist/core/hooks/resolve/transcript.d.ts +16 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +84 -0
- package/dist/core/hooks/shell-rm.d.ts +2 -0
- package/dist/core/hooks/shell-rm.d.ts.map +1 -0
- package/dist/core/hooks/shell-rm.js +105 -0
- package/dist/core/hooks/shell-tokens.d.ts +12 -0
- package/dist/core/hooks/shell-tokens.d.ts.map +1 -0
- package/dist/core/hooks/shell-tokens.js +97 -0
- package/dist/core/hooks/shell-waiter.d.ts.map +1 -1
- package/dist/core/hooks/shell-waiter.js +3 -93
- package/dist/core/semantic/contract.d.ts +5 -5
- package/dist/core/storage/process-log.d.ts +12 -0
- package/dist/core/storage/process-log.d.ts.map +1 -1
- package/dist/core/storage/process-log.js +10 -2
- package/dist/lib/tunnel/allowlist.d.ts +44 -0
- package/dist/lib/tunnel/allowlist.d.ts.map +1 -0
- package/dist/lib/tunnel/allowlist.js +204 -0
- package/dist/lib/tunnel/current-address.d.ts +41 -0
- package/dist/lib/tunnel/current-address.d.ts.map +1 -0
- package/dist/lib/tunnel/current-address.js +123 -0
- package/dist/lib/tunnel/gate.js +13 -6
- package/dist/lib/tunnel/processes.d.ts +39 -0
- package/dist/lib/tunnel/processes.d.ts.map +1 -0
- package/dist/lib/tunnel/processes.js +121 -0
- package/dist/lib/tunnel/state.d.ts +28 -3
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +98 -27
- package/package.json +2 -1
- package/src/commander.ts +32 -0
- package/src/commands/agents.ts +97 -11
- package/src/commands/tunnel.ts +394 -116
- package/src/commands/web-fetch.ts +30 -19
- package/src/commands/web.ts +9 -3
- package/src/core/agents/render/prompt-context.ts +25 -8
- package/src/core/agents/render/session-context.ts +83 -16
- package/src/core/agents/state/names.ts +117 -6
- package/src/core/events/v3/support-storage/pack-reader.ts +45 -19
- package/src/core/hooks/cli.ts +106 -17
- package/src/core/hooks/fork-lineage.ts +82 -0
- package/src/core/hooks/resolve/transcript.ts +88 -0
- package/src/core/hooks/shell-rm.ts +103 -0
- package/src/core/hooks/shell-tokens.ts +96 -0
- package/src/core/hooks/shell-waiter.ts +4 -91
- package/src/core/storage/process-log.ts +17 -2
- package/src/lib/tunnel/allowlist.ts +208 -0
- package/src/lib/tunnel/current-address.ts +158 -0
- package/src/lib/tunnel/gate.ts +16 -8
- package/src/lib/tunnel/processes.ts +149 -0
- package/src/lib/tunnel/state.ts +123 -27
package/src/commands/agents.ts
CHANGED
|
@@ -84,6 +84,7 @@ import {
|
|
|
84
84
|
readLiveCoordinationRow,
|
|
85
85
|
readLiveCoordinationRows,
|
|
86
86
|
} from "../core/agents/state/live-coordination-view.ts";
|
|
87
|
+
import { forkRelationLabels, resolveForkAncestry } from "../core/agents/state/names.ts";
|
|
87
88
|
import {
|
|
88
89
|
type AgentActivity,
|
|
89
90
|
foldSessionState,
|
|
@@ -107,6 +108,7 @@ import {
|
|
|
107
108
|
projectCoordinationViewV3,
|
|
108
109
|
readCoordinationViewV3,
|
|
109
110
|
} from "../core/events/v3/coordination-view.ts";
|
|
111
|
+
import { nativeInstanceIdV3 } from "../core/events/v3/live-route-observer.ts";
|
|
110
112
|
import { liveInstanceIdV3 } from "../core/events/v3/live-routing.ts";
|
|
111
113
|
import {
|
|
112
114
|
countSummarizedSinceV3,
|
|
@@ -437,9 +439,15 @@ export function registerAgentsCommand(
|
|
|
437
439
|
"--session-id <id>",
|
|
438
440
|
"Target the heartbeat with this session_id directly, bypassing the ppid walk.",
|
|
439
441
|
)
|
|
440
|
-
.
|
|
441
|
-
|
|
442
|
-
|
|
442
|
+
.option(
|
|
443
|
+
"--force-ancestor",
|
|
444
|
+
"Allow a fork to change its fork ancestor's lifecycle (only on explicit operator instruction)",
|
|
445
|
+
)
|
|
446
|
+
.action(
|
|
447
|
+
(state: string, opts: { reason?: string; sessionId?: string; forceAncestor?: boolean }) => {
|
|
448
|
+
runLifecycle(state, opts);
|
|
449
|
+
},
|
|
450
|
+
);
|
|
443
451
|
|
|
444
452
|
cmd
|
|
445
453
|
.command("end")
|
|
@@ -453,9 +461,20 @@ export function registerAgentsCommand(
|
|
|
453
461
|
"succeeded | failed | cancelled | timed_out | denied | interrupted | unknown",
|
|
454
462
|
"succeeded",
|
|
455
463
|
)
|
|
456
|
-
.
|
|
457
|
-
|
|
458
|
-
|
|
464
|
+
.option(
|
|
465
|
+
"--force-ancestor",
|
|
466
|
+
"Allow a fork to finalize its fork ancestor's session (only on explicit operator instruction)",
|
|
467
|
+
)
|
|
468
|
+
.action(
|
|
469
|
+
(opts: {
|
|
470
|
+
sessionId?: string;
|
|
471
|
+
instanceId?: string;
|
|
472
|
+
outcome: string;
|
|
473
|
+
forceAncestor?: boolean;
|
|
474
|
+
}) => {
|
|
475
|
+
runEndSession(opts);
|
|
476
|
+
},
|
|
477
|
+
);
|
|
459
478
|
|
|
460
479
|
cmd
|
|
461
480
|
.command("reconcile")
|
|
@@ -666,7 +685,40 @@ const SESSION_OUTCOMES = new Set([
|
|
|
666
685
|
"unknown",
|
|
667
686
|
]);
|
|
668
687
|
|
|
669
|
-
|
|
688
|
+
/**
|
|
689
|
+
* Refuse when the calling session targets one of its recorded fork ancestors.
|
|
690
|
+
* A fork's transcript is full of its parent's name, task, and claims, so a
|
|
691
|
+
* fork that "cleans up its earlier self" is usually cutting off a live parent
|
|
692
|
+
* (2026-10-07: a fork offered to close its running parent). Returns the
|
|
693
|
+
* refusal message, or null when the call may proceed.
|
|
694
|
+
*/
|
|
695
|
+
function forkAncestorRefusal(
|
|
696
|
+
root: string,
|
|
697
|
+
targetIds: Array<string | undefined>,
|
|
698
|
+
force: boolean | undefined,
|
|
699
|
+
action: string,
|
|
700
|
+
): string | null {
|
|
701
|
+
if (force) return null;
|
|
702
|
+
const ids = new Set(targetIds.filter((id): id is string => !!id));
|
|
703
|
+
if (ids.size === 0) return null;
|
|
704
|
+
const self = resolveOwner() ?? sessionIdentityFromEnv();
|
|
705
|
+
if (!self || ids.has(self)) return null;
|
|
706
|
+
const ancestor = resolveForkAncestry(root, self).find((a) => ids.has(a.instance_id));
|
|
707
|
+
if (!ancestor) return null;
|
|
708
|
+
const label = ancestor.name ? `agent-${ancestor.name}` : ancestor.instance_id;
|
|
709
|
+
return (
|
|
710
|
+
`${label} is this session's fork ancestor: this conversation was branched from it, so its ` +
|
|
711
|
+
`name, task, and files in your context are its own, and it may still be running. Refusing to ` +
|
|
712
|
+
`${action} it. Rerun with --force-ancestor only when the operator explicitly asked for that.`
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function runEndSession(opts: {
|
|
717
|
+
sessionId?: string;
|
|
718
|
+
instanceId?: string;
|
|
719
|
+
outcome: string;
|
|
720
|
+
forceAncestor?: boolean;
|
|
721
|
+
}) {
|
|
670
722
|
const root = monorepoRoot();
|
|
671
723
|
if (!root) return failCommand("not_in_repo", "not in an agent session");
|
|
672
724
|
if (!SESSION_OUTCOMES.has(opts.outcome)) {
|
|
@@ -698,6 +750,15 @@ function runEndSession(opts: { sessionId?: string; instanceId?: string; outcome:
|
|
|
698
750
|
return failCommand("session_identity_missing", "live V3 generation disappeared");
|
|
699
751
|
}
|
|
700
752
|
const state = record.state;
|
|
753
|
+
if (opts.instanceId || opts.sessionId) {
|
|
754
|
+
const refusal = forkAncestorRefusal(
|
|
755
|
+
root,
|
|
756
|
+
[target, state.session_id, nativeInstanceIdV3(state.instance_id)],
|
|
757
|
+
opts.forceAncestor,
|
|
758
|
+
"finalize",
|
|
759
|
+
);
|
|
760
|
+
if (refusal) return failCommand("session_is_fork_ancestor", refusal);
|
|
761
|
+
}
|
|
701
762
|
if (state.delegations.length > 0) {
|
|
702
763
|
return failCommand(
|
|
703
764
|
"session_work_open",
|
|
@@ -1992,7 +2053,10 @@ function runSetTask(task: string, opts?: { sessionId?: string }): void {
|
|
|
1992
2053
|
});
|
|
1993
2054
|
}
|
|
1994
2055
|
|
|
1995
|
-
function runLifecycle(
|
|
2056
|
+
function runLifecycle(
|
|
2057
|
+
rawState: string,
|
|
2058
|
+
opts: { reason?: string; sessionId?: string; forceAncestor?: boolean },
|
|
2059
|
+
): void {
|
|
1996
2060
|
const state = rawState.trim().toLowerCase();
|
|
1997
2061
|
if (state !== "active" && state !== "blocked" && state !== "done") {
|
|
1998
2062
|
emit.error({
|
|
@@ -2013,6 +2077,19 @@ function runLifecycle(rawState: string, opts: { reason?: string; sessionId?: str
|
|
|
2013
2077
|
return;
|
|
2014
2078
|
}
|
|
2015
2079
|
if (!opts.sessionId) ensureAdapterSession(root);
|
|
2080
|
+
if (opts.sessionId) {
|
|
2081
|
+
const refusal = forkAncestorRefusal(
|
|
2082
|
+
root,
|
|
2083
|
+
[opts.sessionId],
|
|
2084
|
+
opts.forceAncestor,
|
|
2085
|
+
"change the lifecycle of",
|
|
2086
|
+
);
|
|
2087
|
+
if (refusal) {
|
|
2088
|
+
emit.error({ code: "session_is_fork_ancestor", message: refusal });
|
|
2089
|
+
process.exitCode = 1;
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2016
2093
|
const myOwner = opts.sessionId ?? resolveOwner() ?? sessionIdentityFromEnv();
|
|
2017
2094
|
if (!myOwner) {
|
|
2018
2095
|
emit.error(
|
|
@@ -2390,8 +2467,15 @@ function runStatus(opts: {
|
|
|
2390
2467
|
|
|
2391
2468
|
const { livePeers, stale: peersStale } = collectStatusPeerHealth(root, myOwner);
|
|
2392
2469
|
|
|
2393
|
-
//
|
|
2470
|
+
// Recorded fork relations label the parent and forks so a fork cannot read
|
|
2471
|
+
// its live parent as its own earlier session; they sort first.
|
|
2472
|
+
const relations = forkRelationLabels(root, myOwner, new Set(livePeers.map((p) => p.instance_id)));
|
|
2473
|
+
// Sort: related sessions, then file-holders (by file count desc), then idle
|
|
2474
|
+
// peers by recency.
|
|
2394
2475
|
livePeers.sort((a, b) => {
|
|
2476
|
+
const ar = relations.has(a.instance_id) ? 1 : 0;
|
|
2477
|
+
const br = relations.has(b.instance_id) ? 1 : 0;
|
|
2478
|
+
if (ar !== br) return br - ar;
|
|
2395
2479
|
const af = a.files_touched?.length ?? 0;
|
|
2396
2480
|
const bf = b.files_touched?.length ?? 0;
|
|
2397
2481
|
if (af !== bf) return bf - af;
|
|
@@ -2406,7 +2490,7 @@ function runStatus(opts: {
|
|
|
2406
2490
|
);
|
|
2407
2491
|
// Cross-machine presence (ADR 0016): sessions on other machines, advisory.
|
|
2408
2492
|
const remoteMachines = readRemoteMachines(root);
|
|
2409
|
-
const peersStr = formatPeers(livePeers, 4, peersStale, remoteMachines);
|
|
2493
|
+
const peersStr = formatPeers(livePeers, 4, peersStale, remoteMachines, relations);
|
|
2410
2494
|
|
|
2411
2495
|
const ctxUsage = readContextUsage(hb.native_session_id ?? hb.session_id, hb.platform);
|
|
2412
2496
|
let ctxStr: string;
|
|
@@ -2600,13 +2684,15 @@ function formatPeers(
|
|
|
2600
2684
|
cap: number,
|
|
2601
2685
|
staleCount: number,
|
|
2602
2686
|
remoteMachines: RemoteMachine[] = [],
|
|
2687
|
+
relations: Map<string, string> = new Map(),
|
|
2603
2688
|
): string {
|
|
2604
2689
|
if (peers.length === 0 && staleCount === 0 && remoteMachines.length === 0) return "none";
|
|
2605
2690
|
const labels = peers.map((p) => {
|
|
2606
2691
|
const name = p.name || "unnamed";
|
|
2607
2692
|
const plat = formatPlatformLabel(p.platform);
|
|
2608
2693
|
const files = p.files_touched?.length ?? 0;
|
|
2609
|
-
const
|
|
2694
|
+
const relation = relations.get(p.instance_id);
|
|
2695
|
+
const base = relation ? `${name} (${plat}, ${relation})` : `${name} (${plat})`;
|
|
2610
2696
|
return files > 0 ? `${base}, ${files} files` : base;
|
|
2611
2697
|
});
|
|
2612
2698
|
let main: string;
|