harnery 0.9.0 → 0.10.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 +1 -0
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +6 -0
- package/dist/commands/agents.d.ts +24 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +244 -13
- package/dist/commands/backup.d.ts +5 -4
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +15 -14
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +27 -1
- package/dist/commands/claude-desktop.d.ts +19 -0
- package/dist/commands/claude-desktop.d.ts.map +1 -0
- package/dist/commands/claude-desktop.js +168 -0
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +149 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -3
- package/dist/commands/eml.d.ts +32 -0
- package/dist/commands/eml.d.ts.map +1 -1
- package/dist/commands/eml.js +16 -3
- package/dist/commands/harness.d.ts +11 -0
- package/dist/commands/harness.d.ts.map +1 -0
- package/dist/commands/harness.js +118 -0
- package/dist/commands/presence.d.ts +9 -4
- package/dist/commands/presence.d.ts.map +1 -1
- package/dist/commands/presence.js +88 -5
- package/dist/commands/relay.d.ts +9 -0
- package/dist/commands/relay.d.ts.map +1 -0
- package/dist/commands/relay.js +143 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +5 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +8 -15
- package/dist/core/agents/canonical-emit.d.ts +15 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +41 -3
- package/dist/core/agents/cli.js +4 -2
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +44 -3
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +26 -1
- package/dist/core/agents/rules/commit-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/commit-conflict.js +4 -37
- package/dist/core/agents/session-events.d.ts.map +1 -1
- package/dist/core/agents/session-events.js +16 -43
- package/dist/core/agents/state/heartbeat-projector.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-projector.js +11 -2
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +3 -3
- package/dist/core/agents/state/names.d.ts +38 -3
- package/dist/core/agents/state/names.d.ts.map +1 -1
- package/dist/core/agents/state/names.js +46 -7
- package/dist/core/agents/state/pidmap.d.ts +5 -1
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +58 -2
- package/dist/core/agents/state/stale-sweep.d.ts +3 -2
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +6 -6
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +191 -25
- package/dist/core/context/index.d.ts +144 -0
- package/dist/core/context/index.d.ts.map +1 -0
- package/dist/core/context/index.js +380 -0
- package/dist/core/harnesses/bench.d.ts +29 -0
- package/dist/core/harnesses/bench.d.ts.map +1 -0
- package/dist/core/harnesses/bench.js +151 -0
- package/dist/core/harnesses/index.d.ts +9 -0
- package/dist/core/harnesses/index.d.ts.map +1 -0
- package/dist/core/harnesses/index.js +4 -0
- package/dist/core/harnesses/profiles.d.ts +62 -0
- package/dist/core/harnesses/profiles.d.ts.map +1 -0
- package/dist/core/harnesses/profiles.js +115 -0
- package/dist/core/harnesses/registry.d.ts +15 -0
- package/dist/core/harnesses/registry.d.ts.map +1 -0
- package/dist/core/harnesses/registry.js +113 -0
- package/dist/core/harnesses/types.d.ts +64 -0
- package/dist/core/harnesses/types.d.ts.map +1 -0
- package/dist/core/harnesses/types.js +20 -0
- package/dist/core/hooks/cli.js +280 -63
- package/dist/core/hooks/events/schema.d.ts +38 -1
- package/dist/core/hooks/events/schema.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.d.ts.map +1 -1
- package/dist/core/hooks/harness/events.js +3 -0
- package/dist/core/hooks/harness/parse.d.ts +1 -1
- package/dist/core/hooks/harness/parse.d.ts.map +1 -1
- package/dist/core/hooks/harness/parse.js +4 -0
- package/dist/core/presence/blob.d.ts +40 -0
- package/dist/core/presence/blob.d.ts.map +1 -0
- package/dist/core/presence/blob.js +91 -0
- package/dist/core/presence/git.d.ts +68 -0
- package/dist/core/presence/git.d.ts.map +1 -0
- package/dist/core/presence/git.js +173 -0
- package/dist/core/presence/index.d.ts +74 -0
- package/dist/core/presence/index.d.ts.map +1 -0
- package/dist/core/presence/index.js +224 -0
- package/dist/core/presence/relay-client.d.ts +39 -0
- package/dist/core/presence/relay-client.d.ts.map +1 -0
- package/dist/core/presence/relay-client.js +295 -0
- package/dist/core/presence/relay-protocol.d.ts +94 -0
- package/dist/core/presence/relay-protocol.d.ts.map +1 -0
- package/dist/core/presence/relay-protocol.js +169 -0
- package/dist/core/workflow/billing.d.ts +1 -1
- package/dist/core/workflow/billing.d.ts.map +1 -1
- package/dist/core/workflow/billing.js +7 -1
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +11 -2
- package/dist/core/workflow/harnesses.d.ts +3 -3
- package/dist/core/workflow/harnesses.d.ts.map +1 -1
- package/dist/core/workflow/harnesses.js +8 -17
- package/dist/core/workflow/spawn-claude.d.ts +4 -1
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +39 -18
- package/dist/core/workflow/spawn-codex.d.ts +4 -1
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +44 -22
- package/dist/core/workflow/spawn-cursor.d.ts +4 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +35 -16
- package/dist/core/workflow/types.d.ts +9 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/lib/browser/client.d.ts +16 -0
- package/dist/lib/browser/client.d.ts.map +1 -1
- package/dist/lib/browser/client.js +22 -0
- package/dist/lib/browser/index.d.ts +1 -0
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/index.js +1 -0
- package/dist/lib/browser/launch-args.d.ts +21 -0
- package/dist/lib/browser/launch-args.d.ts.map +1 -0
- package/dist/lib/browser/launch-args.js +32 -0
- package/dist/lib/claude-desktop.d.ts +105 -0
- package/dist/lib/claude-desktop.d.ts.map +1 -0
- package/dist/lib/claude-desktop.js +217 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +5 -2
- package/dist/lib/identities/assume.d.ts +51 -0
- package/dist/lib/identities/assume.d.ts.map +1 -0
- package/dist/lib/identities/assume.js +274 -0
- package/dist/lib/identities/index.d.ts +7 -7
- package/dist/lib/identities/index.d.ts.map +1 -1
- package/dist/lib/identities/index.js +24 -24
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +15 -2
- package/package.json +12 -1
- package/schemas/config.schema.json +88 -19
- package/src/commander.ts +6 -0
- package/src/commands/agents.ts +280 -14
- package/src/commands/backup.ts +17 -16
- package/src/commands/browse.ts +32 -0
- package/src/commands/claude-desktop.ts +215 -0
- package/src/commands/context.ts +174 -1
- package/src/commands/doctor.ts +2 -3
- package/src/commands/eml.ts +22 -4
- package/src/commands/harness.ts +147 -0
- package/src/commands/presence.ts +111 -5
- package/src/commands/relay.ts +160 -0
- package/src/commands/sync.ts +4 -0
- package/src/commands/workflow.ts +9 -18
- package/src/core/agents/canonical-emit.ts +41 -3
- package/src/core/agents/cli.ts +4 -2
- package/src/core/agents/render/prompt-context.ts +48 -3
- package/src/core/agents/render/session-context.ts +27 -1
- package/src/core/agents/rules/commit-conflict.ts +4 -33
- package/src/core/agents/session-events.ts +15 -39
- package/src/core/agents/state/heartbeat-projector.ts +10 -1
- package/src/core/agents/state/heartbeat-writer.ts +3 -2
- package/src/core/agents/state/names.ts +68 -9
- package/src/core/agents/state/pidmap.ts +59 -2
- package/src/core/agents/state/stale-sweep.ts +6 -11
- package/src/core/config.ts +248 -24
- package/src/core/context/index.ts +575 -0
- package/src/core/harnesses/bench.ts +214 -0
- package/src/core/harnesses/index.ts +31 -0
- package/src/core/harnesses/profiles.ts +141 -0
- package/src/core/harnesses/registry.ts +140 -0
- package/src/core/harnesses/types.ts +90 -0
- package/src/core/hooks/cli.ts +331 -62
- package/src/core/hooks/events/schema.ts +68 -0
- package/src/core/hooks/harness/events.ts +3 -0
- package/src/core/hooks/harness/parse.ts +7 -1
- package/src/core/presence/blob.ts +125 -0
- package/src/core/presence/git.ts +191 -0
- package/src/core/presence/index.ts +274 -0
- package/src/core/presence/relay-client.ts +322 -0
- package/src/core/presence/relay-protocol.ts +246 -0
- package/src/core/workflow/billing.ts +8 -2
- package/src/core/workflow/engine.ts +11 -2
- package/src/core/workflow/harnesses.ts +14 -19
- package/src/core/workflow/spawn-claude.ts +39 -20
- package/src/core/workflow/spawn-codex.ts +47 -28
- package/src/core/workflow/spawn-cursor.ts +37 -18
- package/src/core/workflow/types.ts +9 -2
- package/src/lib/browser/client.ts +37 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/launch-args.ts +33 -0
- package/src/lib/claude-desktop.ts +293 -0
- package/src/lib/docs-lint.ts +5 -2
- package/src/lib/identities/assume.ts +363 -0
- package/src/lib/identities/index.ts +28 -24
- package/src/lib/instructions/templates.ts +16 -2
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bind a live session to a durable agent persona.
|
|
3
|
+
*
|
|
4
|
+
* The operation is serialized because "is this name live?" and "claim it"
|
|
5
|
+
* must be one critical section. Authoritative state is written twice on
|
|
6
|
+
* purpose: `.name-history` heals a missing heartbeat, while the canonical
|
|
7
|
+
* `identity.assumed` event makes replay and derived readers converge.
|
|
8
|
+
*
|
|
9
|
+
* Local collision policy: a fresh heartbeat alone is not enough to block.
|
|
10
|
+
* If the namesake has no live pid-map process (crashed session, healed
|
|
11
|
+
* zombie, or abandoned harness), reclaim that heartbeat and continue.
|
|
12
|
+
* Refuse only when another process is still alive, or when cached remote
|
|
13
|
+
* presence reports the name.
|
|
14
|
+
*/
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { emitAndProject } from "../../core/agents/cli-emit.js";
|
|
19
|
+
import { emit } from "../../core/agents/events/emit.js";
|
|
20
|
+
import { readHeartbeat } from "../../core/agents/state/heartbeat-writer.js";
|
|
21
|
+
import { recordNameAssumption } from "../../core/agents/state/names.js";
|
|
22
|
+
import { instanceHasLivePid, removePidmapRowsForInstance } from "../../core/agents/state/pidmap.js";
|
|
23
|
+
import { coordFreshnessSeconds } from "../../core/config.js";
|
|
24
|
+
import { readRemoteMachines } from "../../core/presence/index.js";
|
|
25
|
+
import { bareName, ensureIdentity, lookupById, lookupByName } from "./index.js";
|
|
26
|
+
const ASSUME_LOCK = "identity-assume.lock";
|
|
27
|
+
const ASSUME_LOCK_STALE_MS = 30_000;
|
|
28
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29
|
+
export class IdentityAssumeError extends Error {
|
|
30
|
+
code;
|
|
31
|
+
constructor(code, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.name = "IdentityAssumeError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function heartbeatIsFresh(hb, cutoffMs) {
|
|
38
|
+
const ts = Date.parse(hb.last_heartbeat);
|
|
39
|
+
return Number.isFinite(ts) && ts >= cutoffMs;
|
|
40
|
+
}
|
|
41
|
+
/** Best-effort collision read across local heartbeats and cached remote
|
|
42
|
+
* presence. No network call occurs; absent presence degrades to local-only. */
|
|
43
|
+
export function findIdentityConflict(coordRoot, instanceId, name, nowMs = Date.now()) {
|
|
44
|
+
const wanted = name.toLowerCase();
|
|
45
|
+
const cutoffMs = nowMs - coordFreshnessSeconds(coordRoot) * 1000;
|
|
46
|
+
const activeDir = join(coordRoot, ".harnery", "active");
|
|
47
|
+
if (existsSync(activeDir)) {
|
|
48
|
+
for (const file of readdirSync(activeDir)) {
|
|
49
|
+
if (!file.endsWith(".json"))
|
|
50
|
+
continue;
|
|
51
|
+
try {
|
|
52
|
+
const hb = JSON.parse(readFileSync(join(activeDir, file), "utf8"));
|
|
53
|
+
if (hb.instance_id === instanceId || !heartbeatIsFresh(hb, cutoffMs))
|
|
54
|
+
continue;
|
|
55
|
+
if ((hb.name ?? "").toLowerCase() === wanted) {
|
|
56
|
+
return { instance_id: hb.instance_id, name: hb.name ?? name, scope: "local" };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Malformed heartbeats are handled by stale sweep, not this command.
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const remote of readRemoteMachines(coordRoot)) {
|
|
65
|
+
for (const agent of remote.agents) {
|
|
66
|
+
if (agent.instance_id === instanceId)
|
|
67
|
+
continue;
|
|
68
|
+
if ((agent.name ?? "").toLowerCase() === wanted) {
|
|
69
|
+
return {
|
|
70
|
+
instance_id: agent.instance_id,
|
|
71
|
+
name: agent.name ?? name,
|
|
72
|
+
scope: "remote",
|
|
73
|
+
machine: remote.machine,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Drop a local namesake whose harness process is gone. Fresh heartbeats can
|
|
82
|
+
* linger after a crash (or after heartbeat heal without a live pid-map), and
|
|
83
|
+
* `identity assume` is the supported takeover path — operators should not
|
|
84
|
+
* need a separate kill/sweep step.
|
|
85
|
+
*/
|
|
86
|
+
export function reclaimAbandonedLocalConflict(coordRoot, conflict) {
|
|
87
|
+
if (conflict.scope !== "local")
|
|
88
|
+
return false;
|
|
89
|
+
if (instanceHasLivePid(coordRoot, conflict.instance_id))
|
|
90
|
+
return false;
|
|
91
|
+
const hbPath = join(coordRoot, ".harnery", "active", `${conflict.instance_id}.json`);
|
|
92
|
+
let harness = "claude-code";
|
|
93
|
+
let sessionId = conflict.instance_id;
|
|
94
|
+
let ageSecs;
|
|
95
|
+
if (existsSync(hbPath)) {
|
|
96
|
+
try {
|
|
97
|
+
const hb = JSON.parse(readFileSync(hbPath, "utf8"));
|
|
98
|
+
harness = harnessOf(hb.platform);
|
|
99
|
+
sessionId = hb.session_id || conflict.instance_id;
|
|
100
|
+
const ts = Date.parse(hb.last_heartbeat);
|
|
101
|
+
if (Number.isFinite(ts))
|
|
102
|
+
ageSecs = Math.max(0, Math.floor((Date.now() - ts) / 1000));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* content unreadable — still reclaim the file */
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
unlinkSync(hbPath);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
removePidmapRowsForInstance(coordRoot, conflict.instance_id);
|
|
115
|
+
try {
|
|
116
|
+
emit(coordRoot, {
|
|
117
|
+
event_type: "health.heartbeat_swept",
|
|
118
|
+
instance_id: conflict.instance_id,
|
|
119
|
+
session_id: sessionId,
|
|
120
|
+
harness,
|
|
121
|
+
source: "agent-coord",
|
|
122
|
+
data: {
|
|
123
|
+
reason: "stale",
|
|
124
|
+
...(ageSecs !== undefined ? { age_secs: ageSecs } : {}),
|
|
125
|
+
reclaimed_by: "identity.assume",
|
|
126
|
+
},
|
|
127
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
/* telemetry only */
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
function resolveTarget(coordRoot, input) {
|
|
136
|
+
const trimmed = input.trim();
|
|
137
|
+
if (!trimmed) {
|
|
138
|
+
throw new IdentityAssumeError("invalid_identity", "identity name cannot be empty");
|
|
139
|
+
}
|
|
140
|
+
if (UUID_RE.test(trimmed)) {
|
|
141
|
+
const identity = lookupById(trimmed, coordRoot);
|
|
142
|
+
if (!identity) {
|
|
143
|
+
throw new IdentityAssumeError("identity_not_found", `no durable identity matching '${trimmed}'`);
|
|
144
|
+
}
|
|
145
|
+
return { identity, name: identity.name };
|
|
146
|
+
}
|
|
147
|
+
const name = bareName(trimmed);
|
|
148
|
+
if (!name) {
|
|
149
|
+
throw new IdentityAssumeError("invalid_identity", "identity name cannot be empty");
|
|
150
|
+
}
|
|
151
|
+
const existing = lookupByName(name, coordRoot);
|
|
152
|
+
return { identity: existing, name: existing?.name ?? name };
|
|
153
|
+
}
|
|
154
|
+
function acquireLock(coordRoot) {
|
|
155
|
+
const dir = join(coordRoot, ".harnery");
|
|
156
|
+
const path = join(dir, ASSUME_LOCK);
|
|
157
|
+
mkdirSync(dir, { recursive: true });
|
|
158
|
+
const token = `${process.pid}:${randomUUID()}`;
|
|
159
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
160
|
+
try {
|
|
161
|
+
writeFileSync(path, token, { encoding: "utf8", flag: "wx" });
|
|
162
|
+
return () => {
|
|
163
|
+
try {
|
|
164
|
+
if (readFileSync(path, "utf8") === token)
|
|
165
|
+
unlinkSync(path);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// The critical section completed; a missing lock needs no recovery.
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
if (error.code !== "EEXIST")
|
|
174
|
+
throw error;
|
|
175
|
+
try {
|
|
176
|
+
if (Date.now() - statSync(path).mtimeMs > ASSUME_LOCK_STALE_MS) {
|
|
177
|
+
unlinkSync(path);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
throw new IdentityAssumeError("identity_busy", "another identity assumption is in progress; retry in a moment");
|
|
188
|
+
}
|
|
189
|
+
function harnessOf(platform) {
|
|
190
|
+
if (platform === "cursor")
|
|
191
|
+
return "cursor";
|
|
192
|
+
if (platform === "codex")
|
|
193
|
+
return "codex";
|
|
194
|
+
return "claude-code";
|
|
195
|
+
}
|
|
196
|
+
/** Assume `target` for one live main session. Safe to retry. */
|
|
197
|
+
export function assumeIdentity(coordRoot, instanceId, target) {
|
|
198
|
+
const release = acquireLock(coordRoot);
|
|
199
|
+
try {
|
|
200
|
+
const hb = readHeartbeat(coordRoot, instanceId);
|
|
201
|
+
if (!hb) {
|
|
202
|
+
throw new IdentityAssumeError("no_heartbeat", `no live heartbeat for instance '${instanceId}'`);
|
|
203
|
+
}
|
|
204
|
+
if (hb.kind && hb.kind !== "session") {
|
|
205
|
+
throw new IdentityAssumeError("not_session", `identity assumption is limited to main sessions (current kind: ${hb.kind})`);
|
|
206
|
+
}
|
|
207
|
+
const targetIdentity = resolveTarget(coordRoot, target);
|
|
208
|
+
let reclaimedInstanceId = null;
|
|
209
|
+
let conflict = findIdentityConflict(coordRoot, instanceId, targetIdentity.name);
|
|
210
|
+
if (conflict && reclaimAbandonedLocalConflict(coordRoot, conflict)) {
|
|
211
|
+
reclaimedInstanceId = conflict.instance_id;
|
|
212
|
+
conflict = findIdentityConflict(coordRoot, instanceId, targetIdentity.name);
|
|
213
|
+
}
|
|
214
|
+
if (conflict) {
|
|
215
|
+
const where = conflict.scope === "remote" ? ` on ${conflict.machine}` : "";
|
|
216
|
+
throw new IdentityAssumeError("identity_in_use", `agent-${targetIdentity.name} is already live${where} (${conflict.instance_id}); end or stale-sweep that session before assuming the identity`);
|
|
217
|
+
}
|
|
218
|
+
const identity = targetIdentity.identity ?? ensureIdentity(targetIdentity.name, coordRoot);
|
|
219
|
+
const created = !targetIdentity.identity;
|
|
220
|
+
const previousName = hb.name || null;
|
|
221
|
+
const previousAgentId = hb.agent_id || null;
|
|
222
|
+
const history = recordNameAssumption(coordRoot, instanceId, identity.name, identity.agent_id, "session");
|
|
223
|
+
const changed = history.changed || previousName !== identity.name || previousAgentId !== identity.agent_id;
|
|
224
|
+
if (!changed) {
|
|
225
|
+
return {
|
|
226
|
+
changed: false,
|
|
227
|
+
instance_id: instanceId,
|
|
228
|
+
session_id: hb.session_id,
|
|
229
|
+
previous_name: previousName,
|
|
230
|
+
previous_agent_id: previousAgentId,
|
|
231
|
+
name: identity.name,
|
|
232
|
+
agent_id: identity.agent_id,
|
|
233
|
+
identity_created: false,
|
|
234
|
+
event_id: null,
|
|
235
|
+
reclaimed_instance_id: reclaimedInstanceId,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const emitted = emitAndProject({
|
|
239
|
+
event_type: "identity.assumed",
|
|
240
|
+
instance_id: instanceId,
|
|
241
|
+
session_id: hb.session_id,
|
|
242
|
+
harness: harnessOf(hb.platform),
|
|
243
|
+
data: {
|
|
244
|
+
name: identity.name,
|
|
245
|
+
agent_id: identity.agent_id,
|
|
246
|
+
...(previousName ? { previous_name: previousName } : {}),
|
|
247
|
+
...(previousAgentId ? { previous_agent_id: previousAgentId } : {}),
|
|
248
|
+
...(reclaimedInstanceId ? { reclaimed_instance_id: reclaimedInstanceId } : {}),
|
|
249
|
+
},
|
|
250
|
+
}, { coordRoot });
|
|
251
|
+
if (!emitted?.projected) {
|
|
252
|
+
throw new IdentityAssumeError("projection_failed", "the durable name binding was recorded but event projection failed; rerun the command to heal it");
|
|
253
|
+
}
|
|
254
|
+
const projected = readHeartbeat(coordRoot, instanceId);
|
|
255
|
+
if (projected?.name !== identity.name || projected.agent_id !== identity.agent_id) {
|
|
256
|
+
throw new IdentityAssumeError("projection_failed", "identity.assumed was emitted but the heartbeat did not converge; rerun the command");
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
changed: true,
|
|
260
|
+
instance_id: instanceId,
|
|
261
|
+
session_id: hb.session_id,
|
|
262
|
+
previous_name: previousName,
|
|
263
|
+
previous_agent_id: previousAgentId,
|
|
264
|
+
name: identity.name,
|
|
265
|
+
agent_id: identity.agent_id,
|
|
266
|
+
identity_created: created,
|
|
267
|
+
event_id: emitted.envelope.event_id,
|
|
268
|
+
reclaimed_instance_id: reclaimedInstanceId,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
release();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -39,12 +39,12 @@ export declare function bareName(raw: string): string;
|
|
|
39
39
|
/** Re-add the "agent-" prefix for display contexts. */
|
|
40
40
|
export declare function displayName(name: string): string;
|
|
41
41
|
/** Read one identity by id. Null if missing. */
|
|
42
|
-
export declare function lookupById(agentId: string): AgentIdentity | null;
|
|
42
|
+
export declare function lookupById(agentId: string, coordRoot?: string): AgentIdentity | null;
|
|
43
43
|
/** Read one identity by display name (case-insensitive on bare name).
|
|
44
44
|
* Scans the directory; O(N) on identity count. */
|
|
45
|
-
export declare function lookupByName(name: string): AgentIdentity | null;
|
|
45
|
+
export declare function lookupByName(name: string, coordRoot?: string): AgentIdentity | null;
|
|
46
46
|
/** All known identities, sorted by created_at ascending. */
|
|
47
|
-
export declare function listIdentities(): AgentIdentity[];
|
|
47
|
+
export declare function listIdentities(coordRoot?: string): AgentIdentity[];
|
|
48
48
|
/**
|
|
49
49
|
* Find an identity by name, or mint one. Returns the resolved identity.
|
|
50
50
|
* Idempotent: calling twice with the same name returns the same record.
|
|
@@ -55,15 +55,15 @@ export declare function listIdentities(): AgentIdentity[];
|
|
|
55
55
|
* surface whichever happened to be read first; the second is orphaned.
|
|
56
56
|
* For agent personas (added at human cadence) this race is theoretical.
|
|
57
57
|
*/
|
|
58
|
-
export declare function ensureIdentity(name: string): AgentIdentity;
|
|
58
|
+
export declare function ensureIdentity(name: string, coordRoot?: string): AgentIdentity;
|
|
59
59
|
/** Persist an identity (tmp + rename). Creates the dir if missing. */
|
|
60
|
-
export declare function writeIdentity(id: AgentIdentity): void;
|
|
60
|
+
export declare function writeIdentity(id: AgentIdentity, coordRoot?: string): void;
|
|
61
61
|
/**
|
|
62
62
|
* Rename an existing identity. The prior name lands in aliases[] so name
|
|
63
63
|
* lookup still resolves correctly. The agent_id is stable across renames,
|
|
64
64
|
* which is the whole point of the registry.
|
|
65
65
|
*/
|
|
66
|
-
export declare function renameIdentity(agentId: string, newName: string): AgentIdentity;
|
|
66
|
+
export declare function renameIdentity(agentId: string, newName: string, coordRoot?: string): AgentIdentity;
|
|
67
67
|
/**
|
|
68
68
|
* Resolve a string to an agent_id. Accepts:
|
|
69
69
|
* - A UUID (already an id), verified to exist in registry
|
|
@@ -73,5 +73,5 @@ export declare function renameIdentity(agentId: string, newName: string): AgentI
|
|
|
73
73
|
* Does NOT mint. Callers that want mint-on-miss should call ensureIdentity()
|
|
74
74
|
* with a name, then use the returned agent_id.
|
|
75
75
|
*/
|
|
76
|
-
export declare function resolveAgentId(input: string): string | null;
|
|
76
|
+
export declare function resolveAgentId(input: string, coordRoot?: string): string | null;
|
|
77
77
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/identities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAeH,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,CAAC,CAAC;IAClB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAqCD,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/identities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAeH,eAAO,MAAM,uBAAuB,EAAG,CAAU,CAAC;AAElD,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,CAAC,CAAC;IAClB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;CACpB;AAqCD,2EAA2E;AAC3E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5C;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAMpF;AAED;kDACkD;AAClD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAcnF;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CAWlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,aAAa,CAY9E;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,EAAE,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAMzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,aAAa,CAcf;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU/E"}
|
|
@@ -24,22 +24,22 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFile
|
|
|
24
24
|
import { resolve } from "node:path";
|
|
25
25
|
import { monorepoRoot } from "../../core/agents/index.js";
|
|
26
26
|
export const IDENTITY_SCHEMA_VERSION = 1;
|
|
27
|
-
function identitiesDir() {
|
|
28
|
-
const root = monorepoRoot();
|
|
27
|
+
function identitiesDir(coordRoot) {
|
|
28
|
+
const root = coordRoot ?? monorepoRoot();
|
|
29
29
|
if (!root)
|
|
30
30
|
return null;
|
|
31
31
|
return resolve(root, ".harnery", "identities");
|
|
32
32
|
}
|
|
33
|
-
function ensureDir() {
|
|
34
|
-
const dir = identitiesDir();
|
|
33
|
+
function ensureDir(coordRoot) {
|
|
34
|
+
const dir = identitiesDir(coordRoot);
|
|
35
35
|
if (!dir)
|
|
36
36
|
throw new Error("not in an agent session; no monorepo root");
|
|
37
37
|
if (!existsSync(dir))
|
|
38
38
|
mkdirSync(dir, { recursive: true });
|
|
39
39
|
return dir;
|
|
40
40
|
}
|
|
41
|
-
function identityPath(agentId) {
|
|
42
|
-
return resolve(ensureDir(), `${agentId}.json`);
|
|
41
|
+
function identityPath(agentId, coordRoot) {
|
|
42
|
+
return resolve(ensureDir(coordRoot), `${agentId}.json`);
|
|
43
43
|
}
|
|
44
44
|
function nowIso() {
|
|
45
45
|
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -66,8 +66,8 @@ export function displayName(name) {
|
|
|
66
66
|
return name.startsWith("agent-") ? name : `agent-${name}`;
|
|
67
67
|
}
|
|
68
68
|
/** Read one identity by id. Null if missing. */
|
|
69
|
-
export function lookupById(agentId) {
|
|
70
|
-
const dir = identitiesDir();
|
|
69
|
+
export function lookupById(agentId, coordRoot) {
|
|
70
|
+
const dir = identitiesDir(coordRoot);
|
|
71
71
|
if (!dir)
|
|
72
72
|
return null;
|
|
73
73
|
const fp = resolve(dir, `${agentId}.json`);
|
|
@@ -77,8 +77,8 @@ export function lookupById(agentId) {
|
|
|
77
77
|
}
|
|
78
78
|
/** Read one identity by display name (case-insensitive on bare name).
|
|
79
79
|
* Scans the directory; O(N) on identity count. */
|
|
80
|
-
export function lookupByName(name) {
|
|
81
|
-
const dir = identitiesDir();
|
|
80
|
+
export function lookupByName(name, coordRoot) {
|
|
81
|
+
const dir = identitiesDir(coordRoot);
|
|
82
82
|
if (!dir || !existsSync(dir))
|
|
83
83
|
return null;
|
|
84
84
|
const wanted = bareName(name).toLowerCase();
|
|
@@ -98,8 +98,8 @@ export function lookupByName(name) {
|
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
100
|
/** All known identities, sorted by created_at ascending. */
|
|
101
|
-
export function listIdentities() {
|
|
102
|
-
const dir = identitiesDir();
|
|
101
|
+
export function listIdentities(coordRoot) {
|
|
102
|
+
const dir = identitiesDir(coordRoot);
|
|
103
103
|
if (!dir || !existsSync(dir))
|
|
104
104
|
return [];
|
|
105
105
|
const out = [];
|
|
@@ -123,8 +123,8 @@ export function listIdentities() {
|
|
|
123
123
|
* surface whichever happened to be read first; the second is orphaned.
|
|
124
124
|
* For agent personas (added at human cadence) this race is theoretical.
|
|
125
125
|
*/
|
|
126
|
-
export function ensureIdentity(name) {
|
|
127
|
-
const existing = lookupByName(name);
|
|
126
|
+
export function ensureIdentity(name, coordRoot) {
|
|
127
|
+
const existing = lookupByName(name, coordRoot);
|
|
128
128
|
if (existing)
|
|
129
129
|
return existing;
|
|
130
130
|
const id = {
|
|
@@ -134,13 +134,13 @@ export function ensureIdentity(name) {
|
|
|
134
134
|
aliases: [],
|
|
135
135
|
created_at: nowIso(),
|
|
136
136
|
};
|
|
137
|
-
writeIdentity(id);
|
|
137
|
+
writeIdentity(id, coordRoot);
|
|
138
138
|
return id;
|
|
139
139
|
}
|
|
140
140
|
/** Persist an identity (tmp + rename). Creates the dir if missing. */
|
|
141
|
-
export function writeIdentity(id) {
|
|
142
|
-
ensureDir();
|
|
143
|
-
const fp = identityPath(id.agent_id);
|
|
141
|
+
export function writeIdentity(id, coordRoot) {
|
|
142
|
+
ensureDir(coordRoot);
|
|
143
|
+
const fp = identityPath(id.agent_id, coordRoot);
|
|
144
144
|
const tmp = `${fp}.tmp.${process.pid}`;
|
|
145
145
|
writeFileSync(tmp, `${JSON.stringify(id, null, 2)}\n`, "utf8");
|
|
146
146
|
renameSync(tmp, fp);
|
|
@@ -150,8 +150,8 @@ export function writeIdentity(id) {
|
|
|
150
150
|
* lookup still resolves correctly. The agent_id is stable across renames,
|
|
151
151
|
* which is the whole point of the registry.
|
|
152
152
|
*/
|
|
153
|
-
export function renameIdentity(agentId, newName) {
|
|
154
|
-
const existing = lookupById(agentId);
|
|
153
|
+
export function renameIdentity(agentId, newName, coordRoot) {
|
|
154
|
+
const existing = lookupById(agentId, coordRoot);
|
|
155
155
|
if (!existing) {
|
|
156
156
|
throw new Error(`renameIdentity: no identity matching '${agentId}'`);
|
|
157
157
|
}
|
|
@@ -163,7 +163,7 @@ export function renameIdentity(agentId, newName) {
|
|
|
163
163
|
name: bare,
|
|
164
164
|
aliases: [...existing.aliases, { name: existing.name, retired_at: nowIso() }],
|
|
165
165
|
};
|
|
166
|
-
writeIdentity(next);
|
|
166
|
+
writeIdentity(next, coordRoot);
|
|
167
167
|
return next;
|
|
168
168
|
}
|
|
169
169
|
/**
|
|
@@ -175,16 +175,16 @@ export function renameIdentity(agentId, newName) {
|
|
|
175
175
|
* Does NOT mint. Callers that want mint-on-miss should call ensureIdentity()
|
|
176
176
|
* with a name, then use the returned agent_id.
|
|
177
177
|
*/
|
|
178
|
-
export function resolveAgentId(input) {
|
|
178
|
+
export function resolveAgentId(input, coordRoot) {
|
|
179
179
|
const trimmed = input.trim();
|
|
180
180
|
if (!trimmed)
|
|
181
181
|
return null;
|
|
182
182
|
// UUID shape check, relaxed; we only need the lookupById to confirm.
|
|
183
183
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmed)) {
|
|
184
|
-
const byId = lookupById(trimmed);
|
|
184
|
+
const byId = lookupById(trimmed, coordRoot);
|
|
185
185
|
if (byId)
|
|
186
186
|
return byId.agent_id;
|
|
187
187
|
}
|
|
188
|
-
const byName = lookupByName(trimmed);
|
|
188
|
+
const byName = lookupByName(trimmed, coordRoot);
|
|
189
189
|
return byName ? byName.agent_id : null;
|
|
190
190
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/lib/instructions/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAClD,sEAAsE;AACtE,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,MAAM,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,WAA6C,GACpD,MAAM,CA4DR;AAID,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAsKD,eAAO,MAAM,MAAM,EAAE,aAAa,EA4BjC,CAAC"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* files), so they compile into `dist/` and resolve identically under Bun and
|
|
11
11
|
* Node — no `files`-field copy or package-path guesswork.
|
|
12
12
|
*/
|
|
13
|
+
import { SCRATCH_CATEGORIES } from "../../core/scratch/index.js";
|
|
13
14
|
import { buildOwnedSkill } from "./splice.js";
|
|
14
15
|
/** Managed-region name for the AGENTS.md orientation block. */
|
|
15
16
|
export const INSTRUCTIONS_REGION = "instructions";
|
|
@@ -39,6 +40,12 @@ export function renderInstructionsBlock(binName, skills = { decide: true, counci
|
|
|
39
40
|
const councilPointer = skills.council
|
|
40
41
|
? "The `harn-council` skill has the steward and member flow."
|
|
41
42
|
: `See \`${b} council --help\` for the steward and member flow.`;
|
|
43
|
+
// Render the scratch categories from the canonical enum so this prose can
|
|
44
|
+
// never drift from what `scratch add` actually accepts (the "note, plan…" list
|
|
45
|
+
// silently lagged the tool by two categories before this).
|
|
46
|
+
const scratchCats = SCRATCH_CATEGORIES.length > 1
|
|
47
|
+
? `${SCRATCH_CATEGORIES.slice(0, -1).join(", ")}, or ${SCRATCH_CATEGORIES.at(-1)}`
|
|
48
|
+
: SCRATCH_CATEGORIES[0];
|
|
42
49
|
return `## harnery coordination
|
|
43
50
|
|
|
44
51
|
This project runs [harnery](https://harnery.com) for multi-agent coordination.
|
|
@@ -52,13 +59,19 @@ active peers and the files they've claimed; \`${b} agents set-task "<focus>"\`
|
|
|
52
59
|
declares your current focus so peers can see it. Check for peers before editing
|
|
53
60
|
widely-shared files.
|
|
54
61
|
|
|
62
|
+
**Durable role handoff.** When you are replacing a prior session in the same
|
|
63
|
+
named role, run \`${b} agents identity assume <name>\` before declaring your task.
|
|
64
|
+
It reclaims an abandoned namesake (no live process) and refuses only when another
|
|
65
|
+
live process still holds the name; never hand-edit Harnery's history, heartbeat,
|
|
66
|
+
or derived identity cache.
|
|
67
|
+
|
|
55
68
|
**Declare intent on shell commands.** Every command you run is captured to the
|
|
56
69
|
coordination ledger. Lead a shell command with a \`# intent: <why>\` comment (or set
|
|
57
70
|
the tool's description) so the recorded event carries a reason instead of
|
|
58
71
|
\`(no intent)\`.
|
|
59
72
|
|
|
60
|
-
**Scratch journal.** \`${b} scratch add <category> "<text>"\` (category =
|
|
61
|
-
|
|
73
|
+
**Scratch journal.** \`${b} scratch add <category> "<text>"\` (category = ${scratchCats})
|
|
74
|
+
leaves breadcrumbs that survive context compaction;
|
|
62
75
|
\`${b} scratch read\` reads yours, \`${b} scratch read --name <peer>\` reads a peer's.
|
|
63
76
|
Use it for anything future-you or a peer will need to pick up your thread.
|
|
64
77
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harnery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Multi-agent coordination for AI coding agents - Claude Code, Cursor, and Codex.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryan Kelly",
|
|
@@ -52,6 +52,16 @@
|
|
|
52
52
|
"types": "./dist/core/hooks/index.d.ts",
|
|
53
53
|
"import": "./dist/core/hooks/index.js"
|
|
54
54
|
},
|
|
55
|
+
"./core/harnesses": {
|
|
56
|
+
"bun": "./src/core/harnesses/index.ts",
|
|
57
|
+
"types": "./dist/core/harnesses/index.d.ts",
|
|
58
|
+
"import": "./dist/core/harnesses/index.js"
|
|
59
|
+
},
|
|
60
|
+
"./core/context": {
|
|
61
|
+
"bun": "./src/core/context/index.ts",
|
|
62
|
+
"types": "./dist/core/context/index.d.ts",
|
|
63
|
+
"import": "./dist/core/context/index.js"
|
|
64
|
+
},
|
|
55
65
|
"./lib/agent-browser": {
|
|
56
66
|
"bun": "./src/lib/agent-browser/index.ts",
|
|
57
67
|
"types": "./dist/lib/agent-browser/index.d.ts",
|
|
@@ -129,6 +139,7 @@
|
|
|
129
139
|
"typecheck": "bun x tsc --noEmit",
|
|
130
140
|
"lint": "bun x @biomejs/biome check src",
|
|
131
141
|
"check:portability": "bun run scripts/check-portability.ts",
|
|
142
|
+
"check:public-surface": "bun run scripts/check-public-surface.ts",
|
|
132
143
|
"check:layering": "bun run scripts/check-layering.ts",
|
|
133
144
|
"lint:fix": "bun x @biomejs/biome check --write src",
|
|
134
145
|
"format": "bun x @biomejs/biome format --write src",
|