primcli 1.67.0 → 1.69.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 +34 -10
- package/bin/claude-pending-mail.mjs +58 -1
- package/bin/claude-wake.mjs +6 -1
- package/dist/{api-command-Dh4hs7kh.js → api-command-C58Oh38I.js} +76 -14
- package/dist/{machine-session-s6LvMvRd.js → machine-session-CKmn5bvB.js} +359 -37
- package/dist/{notify-session-CeKAcPIX.js → notify-session-CVZtEboh.js} +1 -1
- package/dist/oclif/auto-signal-worker.js +2 -2
- package/dist/oclif/index.js +120 -45
- package/dist/oclif/receiver-heal.js +1 -1
- package/dist/{signal-command-BxafnMiU.js → signal-command-Dy_oub7_.js} +1 -1
- package/dist/skills/primitive-connect/SKILL.md +6 -0
- package/dist/skills/primitive-connect/package-lock.json +78 -78
- package/dist/skills/primitive-connect/package.json +1 -1
- package/dist/skills/primitive-connect/references/conversations.md +22 -1
- package/dist/skills/primitive-connect.json +6 -6
- package/package.json +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { S as __exportAll } from "./repeats-BL7ZC-_6-BZ-MphTV.js";
|
|
2
2
|
import { B as parseOwnerMemberAddress, F as agentProfilesDirectory, I as connectedAgentIdentity, L as connectedApiBaseUrl, M as AgentConnectionSetupError, N as agentProfileDirectory, P as agentProfileName, R as loadConnectedAgentProfile, U as saveConnectedAgentProfile, V as profileAlreadyConnectedMessage, W as PrimitiveApiClient, j as AGENT_PROFILE_ENV, x as loadCliCredentials, z as parseConnectedAgentProfile } from "./cli-config-DMszFbGn.js";
|
|
3
|
-
import { Ct as cliInvocation, Jn as putAgentContactPolicy, Jt as refreshStoredCliCredentials, Pn as listDomains, Tt as defaultConnectionName, Vn as listSentEmails, Yn as putContact, an as deleteAgentContact, ar as sendEmail, jn as listContacts, kn as listAgentContacts, mn as getContact, nr as searchEmails, on as deleteContact, pn as getAgentContactPolicy, qn as putAgentContact, vn as getEmail } from "./api-command-
|
|
3
|
+
import { Ct as cliInvocation, Jn as putAgentContactPolicy, Jt as refreshStoredCliCredentials, Pn as listDomains, Tt as defaultConnectionName, Vn as listSentEmails, Yn as putContact, an as deleteAgentContact, ar as sendEmail, jn as listContacts, kn as listAgentContacts, mn as getContact, nr as searchEmails, on as deleteContact, pn as getAgentContactPolicy, qn as putAgentContact, vn as getEmail } from "./api-command-C58Oh38I.js";
|
|
4
4
|
import { _ as acquireListenLock, c as mailTime, d as readMailJson, f as removeMailFile, g as ListenStateError, h as writeMailJson, i as mailId, l as privateMailDirectory, o as mailObject, r as mailAddress, s as mailString } from "./shared-mail-files-MwlVE90c.js";
|
|
5
|
-
import { _ as detectAgentRuntime, a as NotificationRetryError, d as defaultSessionSocket, f as NativeSessionDisconnectedError, l as SESSION_UUID, t as notificationScope, u as connectNativeSession, v as installConnectSkill, x as readBundledConnectSkill } from "./notify-session-
|
|
5
|
+
import { _ as detectAgentRuntime, a as NotificationRetryError, d as defaultSessionSocket, f as NativeSessionDisconnectedError, l as SESSION_UUID, t as notificationScope, u as connectNativeSession, v as installConnectSkill, x as readBundledConnectSkill } from "./notify-session-CVZtEboh.js";
|
|
6
6
|
import { a as scopedChatSenderTrust } from "./scoped-chat-Dsark9hm.js";
|
|
7
7
|
import { l as stopBackgroundListen, r as backgroundListenStatus } from "./listen-background--0peztKE.js";
|
|
8
8
|
import { t as readSharedMailOwner } from "./shared-mail-watch-DtKpsgg8.js";
|
|
@@ -1674,8 +1674,9 @@ function readSessionRecord(configDir, session) {
|
|
|
1674
1674
|
agentInfo: row.agentInfo === "created" || row.agentInfo === "already_present" ? row.agentInfo : null,
|
|
1675
1675
|
registeredAt: typeof row.registeredAt === "string" ? row.registeredAt : (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
1676
1676
|
endedAt: typeof row.endedAt === "string" ? row.endedAt : null,
|
|
1677
|
-
disconnect: row.disconnect === "done" || row.disconnect === "pending" || row.disconnect === "pending_enrollment" || row.disconnect === "deferred" ? row.disconnect : typeof row.endedAt === "string" ? "done" : null,
|
|
1677
|
+
disconnect: row.disconnect === "done" || row.disconnect === "pending" || row.disconnect === "pending_enrollment" || row.disconnect === "deferred" || row.disconnect === "kept" ? row.disconnect : typeof row.endedAt === "string" ? "done" : null,
|
|
1678
1678
|
deferredUntil: typeof row.deferredUntil === "string" ? row.deferredUntil : null,
|
|
1679
|
+
revokeOnEnd: row.revokeOnEnd === true,
|
|
1679
1680
|
endReason: typeof row.endReason === "string" && REASON.test(row.endReason) ? row.endReason : null,
|
|
1680
1681
|
release: parseRelease(row.release)
|
|
1681
1682
|
};
|
|
@@ -1741,6 +1742,7 @@ function releasedRecord(record, reason, at, current) {
|
|
|
1741
1742
|
endedAt: null,
|
|
1742
1743
|
disconnect: null,
|
|
1743
1744
|
deferredUntil: null,
|
|
1745
|
+
revokeOnEnd: false,
|
|
1744
1746
|
endReason: null,
|
|
1745
1747
|
release: {
|
|
1746
1748
|
at: at.toISOString(),
|
|
@@ -2505,7 +2507,7 @@ async function runAddressNotesRequest(client, request) {
|
|
|
2505
2507
|
if (request.ifAbsent) condition = { if_absent: true };
|
|
2506
2508
|
else if (ifVersion) condition = { if_version: ifVersion };
|
|
2507
2509
|
else try {
|
|
2508
|
-
condition = { if_version: (await getNote(client, address, name)).version };
|
|
2510
|
+
condition = { if_version: (await getNote(client, address, name, request.signal)).version };
|
|
2509
2511
|
} catch (error) {
|
|
2510
2512
|
if (!(error instanceof AddressNotesApiError) || error.status !== 404) throw error;
|
|
2511
2513
|
condition = { if_absent: true };
|
|
@@ -2523,10 +2525,11 @@ async function runAddressNotesRequest(client, request) {
|
|
|
2523
2525
|
...request.visibility ? { visibility: request.visibility } : {}
|
|
2524
2526
|
},
|
|
2525
2527
|
headers: { "Content-Type": "application/json" },
|
|
2526
|
-
responseStyle: "fields"
|
|
2528
|
+
responseStyle: "fields",
|
|
2529
|
+
...request.signal ? { signal: request.signal } : {}
|
|
2527
2530
|
})), address, name);
|
|
2528
2531
|
}
|
|
2529
|
-
const version = ifVersion ?? (await getNote(client, address, name)).version;
|
|
2532
|
+
const version = ifVersion ?? (await getNote(client, address, name, request.signal)).version;
|
|
2530
2533
|
const data = await result(client.delete({
|
|
2531
2534
|
security,
|
|
2532
2535
|
url: "/address-notes/{address}/{name}",
|
|
@@ -2535,12 +2538,127 @@ async function runAddressNotesRequest(client, request) {
|
|
|
2535
2538
|
name
|
|
2536
2539
|
},
|
|
2537
2540
|
query: { if_version: version },
|
|
2538
|
-
responseStyle: "fields"
|
|
2541
|
+
responseStyle: "fields",
|
|
2542
|
+
...request.signal ? { signal: request.signal } : {}
|
|
2539
2543
|
}));
|
|
2540
2544
|
if (!data || typeof data !== "object" || Array.isArray(data) || typeof data.deleted !== "boolean") throw new Error("The address notes API returned an invalid deletion response.");
|
|
2541
2545
|
return data;
|
|
2542
2546
|
}
|
|
2543
2547
|
//#endregion
|
|
2548
|
+
//#region src/oclif/agent-resume-note.ts
|
|
2549
|
+
/**
|
|
2550
|
+
* The address note holding the command that resumes the coding session an
|
|
2551
|
+
* agent is bound to, so an app can offer to reopen that exact session.
|
|
2552
|
+
*/
|
|
2553
|
+
const RESUME_NOTE_NAME = "AGENT_RESUME";
|
|
2554
|
+
/**
|
|
2555
|
+
* Each note request gets this long. The note is best effort and runs inside
|
|
2556
|
+
* registration, connect, disconnect and their hooks, so a stalled request
|
|
2557
|
+
* must never hold them up.
|
|
2558
|
+
*/
|
|
2559
|
+
const NOTE_REQUEST_TIMEOUT_MS = 1e4;
|
|
2560
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2561
|
+
/**
|
|
2562
|
+
* The AGENT_RESUME value for a session: `claude --resume <uuid>` for Claude
|
|
2563
|
+
* Code or `codex resume <uuid>` for Codex, with the ID lowercased. Any other
|
|
2564
|
+
* runtime, or an ID that is not a UUID, has no value.
|
|
2565
|
+
*/
|
|
2566
|
+
function resumeNoteValue(runtime, sessionId) {
|
|
2567
|
+
const id = sessionId?.trim().toLowerCase() ?? "";
|
|
2568
|
+
if (!UUID.test(id)) return null;
|
|
2569
|
+
if (runtime === "claude") return `claude --resume ${id}`;
|
|
2570
|
+
if (runtime === "codex") return `codex resume ${id}`;
|
|
2571
|
+
return null;
|
|
2572
|
+
}
|
|
2573
|
+
function client(profile, fetchImpl) {
|
|
2574
|
+
return new PrimitiveApiClient({
|
|
2575
|
+
apiKey: profile.api_key,
|
|
2576
|
+
apiBaseUrl: profile.api_base_url,
|
|
2577
|
+
...fetchImpl ? { fetch: fetchImpl } : {}
|
|
2578
|
+
}).client;
|
|
2579
|
+
}
|
|
2580
|
+
/** The current note, or null when the address has none. */
|
|
2581
|
+
async function currentNote(api, address, timeoutMs) {
|
|
2582
|
+
try {
|
|
2583
|
+
return await runAddressNotesRequest(api, {
|
|
2584
|
+
action: "get",
|
|
2585
|
+
address,
|
|
2586
|
+
name: RESUME_NOTE_NAME,
|
|
2587
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
2588
|
+
});
|
|
2589
|
+
} catch (error) {
|
|
2590
|
+
if (error instanceof AddressNotesApiError && error.status === 404) return null;
|
|
2591
|
+
throw error;
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* Write the AGENT_RESUME note for a connected profile's address. Skips the
|
|
2596
|
+
* write when the note already holds the same value, so a resume hook that
|
|
2597
|
+
* runs on every start does not rewrite it. `rewrite` writes it anyway, so a
|
|
2598
|
+
* new claim leaves the note no older than the connection it belongs to.
|
|
2599
|
+
* Never throws.
|
|
2600
|
+
*/
|
|
2601
|
+
async function publishResumeNote(options) {
|
|
2602
|
+
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
2603
|
+
const value = resumeNoteValue(options.runtime, options.sessionId);
|
|
2604
|
+
if (value === null) return "skipped";
|
|
2605
|
+
try {
|
|
2606
|
+
const profile = loadConnectedAgentProfile(options.configDir, options.profileName);
|
|
2607
|
+
if (!profile) return "skipped";
|
|
2608
|
+
const api = client(profile, options.fetch);
|
|
2609
|
+
const address = profile.agent_address;
|
|
2610
|
+
const current = await currentNote(api, address, timeoutMs);
|
|
2611
|
+
if (current?.value === value && !options.rewrite) return "unchanged";
|
|
2612
|
+
await runAddressNotesRequest(api, {
|
|
2613
|
+
action: "set",
|
|
2614
|
+
address,
|
|
2615
|
+
name: RESUME_NOTE_NAME,
|
|
2616
|
+
value,
|
|
2617
|
+
visibility: "private",
|
|
2618
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
2619
|
+
...current?.version ? { ifVersion: current.version } : { ifAbsent: true }
|
|
2620
|
+
});
|
|
2621
|
+
return "written";
|
|
2622
|
+
} catch {
|
|
2623
|
+
return "failed";
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Delete the AGENT_RESUME note with this credential, before it is revoked.
|
|
2628
|
+
* Never throws.
|
|
2629
|
+
*/
|
|
2630
|
+
async function clearResumeNote(profile, fetchImpl, timeoutMs = NOTE_REQUEST_TIMEOUT_MS) {
|
|
2631
|
+
try {
|
|
2632
|
+
const api = client(profile, fetchImpl);
|
|
2633
|
+
const current = await currentNote(api, profile.agent_address, timeoutMs);
|
|
2634
|
+
if (!current) return "absent";
|
|
2635
|
+
await runAddressNotesRequest(api, {
|
|
2636
|
+
action: "delete",
|
|
2637
|
+
address: profile.agent_address,
|
|
2638
|
+
name: RESUME_NOTE_NAME,
|
|
2639
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
2640
|
+
...current.version ? { ifVersion: current.version } : {}
|
|
2641
|
+
});
|
|
2642
|
+
return "cleared";
|
|
2643
|
+
} catch {
|
|
2644
|
+
return "failed";
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* Delete the AGENT_RESUME note of a connected profile's address, for a
|
|
2649
|
+
* connection that binds no resumable session, so a command left by an older
|
|
2650
|
+
* session never points at the wrong conversation. Never throws.
|
|
2651
|
+
*/
|
|
2652
|
+
async function clearProfileResumeNote(options) {
|
|
2653
|
+
try {
|
|
2654
|
+
const profile = loadConnectedAgentProfile(options.configDir, options.profileName);
|
|
2655
|
+
if (!profile) return "skipped";
|
|
2656
|
+
return await clearResumeNote(profile, options.fetch);
|
|
2657
|
+
} catch {
|
|
2658
|
+
return "failed";
|
|
2659
|
+
}
|
|
2660
|
+
}
|
|
2661
|
+
//#endregion
|
|
2544
2662
|
//#region src/oclif/agent-disconnect.ts
|
|
2545
2663
|
var AgentDisconnectError = class extends Error {};
|
|
2546
2664
|
/** The profile no longer holds the credential the caller expected to revoke. */
|
|
@@ -2674,6 +2792,12 @@ async function disconnectAgent(params) {
|
|
|
2674
2792
|
}) : null;
|
|
2675
2793
|
if (receiver && !receiverStopped(receiver)) throw new AgentDisconnectError("The bound receiver has not confirmed stopping. The connection and credential were preserved. Check listener status before retrying.");
|
|
2676
2794
|
const previous = confirmedLocally(directory, profile);
|
|
2795
|
+
let resumeNote = "skipped";
|
|
2796
|
+
if (!previous) try {
|
|
2797
|
+
resumeNote = await (params.clearResumeNote ?? clearResumeNote)(profile, params.fetch);
|
|
2798
|
+
} catch {
|
|
2799
|
+
resumeNote = "failed";
|
|
2800
|
+
}
|
|
2677
2801
|
const revocation = previous ? "previously_confirmed" : await revoke(profile, params.fetch);
|
|
2678
2802
|
if (!previous) try {
|
|
2679
2803
|
writeMailJson(confirmationPath(directory, profile), {
|
|
@@ -2706,7 +2830,8 @@ async function disconnectAgent(params) {
|
|
|
2706
2830
|
identity: connectedAgentIdentity(profileName, profile),
|
|
2707
2831
|
receiver,
|
|
2708
2832
|
revocation,
|
|
2709
|
-
externalHook
|
|
2833
|
+
externalHook,
|
|
2834
|
+
resumeNote
|
|
2710
2835
|
};
|
|
2711
2836
|
} catch (error) {
|
|
2712
2837
|
if (error instanceof AgentDisconnectError) throw error;
|
|
@@ -2816,6 +2941,7 @@ async function clearRevokedAddresses(params) {
|
|
|
2816
2941
|
stopReceiver: params.stopReceiver,
|
|
2817
2942
|
now: params.now,
|
|
2818
2943
|
env: params.env,
|
|
2944
|
+
clearResumeNote: async () => "skipped",
|
|
2819
2945
|
expected: {
|
|
2820
2946
|
address: checked.agent_address,
|
|
2821
2947
|
invitationHash: checked.invitation_hash
|
|
@@ -4026,6 +4152,17 @@ function defaults(options) {
|
|
|
4026
4152
|
});
|
|
4027
4153
|
},
|
|
4028
4154
|
seedAgentInfo: (profileName, value) => seedAgentInfoNote(options.configDir, profileName, value),
|
|
4155
|
+
publishResumeNote: (profileName, runtime, session) => publishResumeNote({
|
|
4156
|
+
configDir: options.configDir,
|
|
4157
|
+
profileName,
|
|
4158
|
+
runtime,
|
|
4159
|
+
sessionId: session,
|
|
4160
|
+
rewrite: true
|
|
4161
|
+
}),
|
|
4162
|
+
clearResumeNote: (profileName) => clearProfileResumeNote({
|
|
4163
|
+
configDir: options.configDir,
|
|
4164
|
+
profileName
|
|
4165
|
+
}),
|
|
4029
4166
|
refreshOwnerMemberAddress: (profileName) => refreshOwnerMemberAddress({
|
|
4030
4167
|
configDir: options.configDir,
|
|
4031
4168
|
profileName
|
|
@@ -4222,6 +4359,12 @@ async function runAgentConnect(options) {
|
|
|
4222
4359
|
reason: "already_present"
|
|
4223
4360
|
});
|
|
4224
4361
|
}
|
|
4362
|
+
let resumeNote = "skipped";
|
|
4363
|
+
if (verified) try {
|
|
4364
|
+
resumeNote = session && runtime ? await dependencies.publishResumeNote(result.identity.profileName, runtime, session) : await dependencies.clearResumeNote(result.identity.profileName);
|
|
4365
|
+
} catch {
|
|
4366
|
+
resumeNote = "failed";
|
|
4367
|
+
}
|
|
4225
4368
|
const receiving = receiver === "external" ? {
|
|
4226
4369
|
...result.receiving,
|
|
4227
4370
|
state: externalReceivingState(externalHook),
|
|
@@ -4277,6 +4420,7 @@ async function runAgentConnect(options) {
|
|
|
4277
4420
|
receiving,
|
|
4278
4421
|
...result.ownerNotifications ? { ownerNotifications: result.ownerNotifications } : {},
|
|
4279
4422
|
agentInfo: agentInfoState,
|
|
4423
|
+
resumeNote,
|
|
4280
4424
|
skipped,
|
|
4281
4425
|
warnings,
|
|
4282
4426
|
selectProfile: `PRIMITIVE_AGENT_PROFILE=${result.identity.profileName}`,
|
|
@@ -4799,6 +4943,8 @@ async function enrollAgent(params) {
|
|
|
4799
4943
|
var machine_session_exports = /* @__PURE__ */ __exportAll({
|
|
4800
4944
|
HOOK_END_GRACE_MS: () => HOOK_END_GRACE_MS,
|
|
4801
4945
|
OMP_MAIN: () => OMP_MAIN,
|
|
4946
|
+
RECONNECT_HINT: () => RECONNECT_HINT,
|
|
4947
|
+
STALE_SESSION_MS: () => STALE_SESSION_MS,
|
|
4802
4948
|
agentConnectionState: () => agentConnectionState,
|
|
4803
4949
|
agentInfoForSession: () => agentInfoForSession,
|
|
4804
4950
|
connectedProfilesForSession: () => connectedProfilesForSession,
|
|
@@ -4811,9 +4957,11 @@ var machine_session_exports = /* @__PURE__ */ __exportAll({
|
|
|
4811
4957
|
readClaudeHookInput: () => readClaudeHookInput,
|
|
4812
4958
|
registerSession: () => registerSession,
|
|
4813
4959
|
retryPendingDisconnects: () => retryPendingDisconnects,
|
|
4960
|
+
revokeStaleSessionAgents: () => revokeStaleSessionAgents,
|
|
4814
4961
|
revokeWithMemberLogin: () => revokeWithMemberLogin,
|
|
4815
4962
|
runDetached: () => runDetached,
|
|
4816
4963
|
runtimeSessionId: () => runtimeSessionId,
|
|
4964
|
+
staleSessionAgents: () => staleSessionAgents,
|
|
4817
4965
|
workspaceName: () => workspaceName
|
|
4818
4966
|
});
|
|
4819
4967
|
/** The repository (or directory) a session works in, for its default name. */
|
|
@@ -5158,7 +5306,7 @@ async function cleanUpReplacedRegistration(configDir, record, current, deps, env
|
|
|
5158
5306
|
await deps.revokeByAddress(configDir, address, fetchImpl);
|
|
5159
5307
|
} catch {}
|
|
5160
5308
|
}
|
|
5161
|
-
async function disconnectSessionAgent(configDir, record, deps, env, fetchImpl, enrollmentSettled = false) {
|
|
5309
|
+
async function disconnectSessionAgent(configDir, record, deps, env, fetchImpl, enrollmentSettled = false, report = {}) {
|
|
5162
5310
|
try {
|
|
5163
5311
|
const current = loadConnectedAgentProfile(configDir, record.profile);
|
|
5164
5312
|
if (current) {
|
|
@@ -5168,7 +5316,7 @@ async function disconnectSessionAgent(configDir, record, deps, env, fetchImpl, e
|
|
|
5168
5316
|
};
|
|
5169
5317
|
if (!holdsRegisteredCredential(configDir, record, current)) return await release(current);
|
|
5170
5318
|
try {
|
|
5171
|
-
await deps.disconnect({
|
|
5319
|
+
const disconnected = await deps.disconnect({
|
|
5172
5320
|
configDir,
|
|
5173
5321
|
profileName: record.profile,
|
|
5174
5322
|
fetch: fetchImpl,
|
|
@@ -5178,6 +5326,7 @@ async function disconnectSessionAgent(configDir, record, deps, env, fetchImpl, e
|
|
|
5178
5326
|
invitationHash: current.invitation_hash
|
|
5179
5327
|
}
|
|
5180
5328
|
});
|
|
5329
|
+
if (disconnected?.resumeNote) report.resumeNote = disconnected.resumeNote;
|
|
5181
5330
|
} catch (error) {
|
|
5182
5331
|
if (!(error instanceof AgentCredentialChangedError)) throw error;
|
|
5183
5332
|
const holder = loadConnectedAgentProfile(configDir, record.profile);
|
|
@@ -5197,8 +5346,8 @@ async function disconnectSessionAgent(configDir, record, deps, env, fetchImpl, e
|
|
|
5197
5346
|
return "pending";
|
|
5198
5347
|
}
|
|
5199
5348
|
}
|
|
5200
|
-
async function finishSessionDisconnect(configDir, record, deps, env, fetchImpl, enrollmentSettled = false) {
|
|
5201
|
-
const outcome = await disconnectSessionAgent(configDir, record, deps, env, fetchImpl, enrollmentSettled);
|
|
5349
|
+
async function finishSessionDisconnect(configDir, record, deps, env, fetchImpl, enrollmentSettled = false, report = {}) {
|
|
5350
|
+
const outcome = await disconnectSessionAgent(configDir, record, deps, env, fetchImpl, enrollmentSettled, report);
|
|
5202
5351
|
const saved = await updateSessionRecord(configDir, record.session, (current) => {
|
|
5203
5352
|
if (current?.createdBy !== "session-register" || current.disconnect === "done") return current;
|
|
5204
5353
|
if (outcome === "released") return releasedRecord(current, "credential_changed", deps.now());
|
|
@@ -5221,8 +5370,15 @@ function withDefined(defaults, overrides) {
|
|
|
5221
5370
|
...Object.fromEntries(Object.entries(overrides ?? {}).filter(([, value]) => value !== void 0))
|
|
5222
5371
|
};
|
|
5223
5372
|
}
|
|
5373
|
+
/**
|
|
5374
|
+
* What to do about an agent that was disconnected in Primitive. The owner's
|
|
5375
|
+
* Reconnect re-invites the same address, and pasting that instruction into
|
|
5376
|
+
* the session claims it into the session's profile again.
|
|
5377
|
+
*/
|
|
5378
|
+
const RECONNECT_HINT = "To get the same address back, the owner can use Reconnect on this agent in the Primitive app and paste the instruction it gives into this session.";
|
|
5224
5379
|
const HELD_DETAIL = "This session already receives as another connected profile, so this profile's receive hooks were not added. Run `primitive machine doctor --fix --profile <profile>` to bind it deliberately.";
|
|
5225
|
-
const ENDED_DETAIL =
|
|
5380
|
+
const ENDED_DETAIL = `This session ended and its agent was disconnected. A new address is never created for the same session. ${RECONNECT_HINT}`;
|
|
5381
|
+
const ENDED_KEPT_DETAIL = "This session ended while it was registering. Its agent is kept, and resuming the session reconnects it at the same address.";
|
|
5226
5382
|
const ENDED_DEFERRED_DETAIL = "This session ended moments ago. Its agent is disconnected after a short wait unless the session starts again first.";
|
|
5227
5383
|
const RELEASED_DETAIL = "This session's profile now holds an agent connected some other way, so it was left connected.";
|
|
5228
5384
|
const ENDED_PENDING_DETAIL = "This session ended, but disconnecting its agent has not been confirmed yet; `primitive machine doctor --fix` retries it.";
|
|
@@ -5239,6 +5395,13 @@ async function registerSession(options) {
|
|
|
5239
5395
|
installClaudeHook: installClaudeWakeHook,
|
|
5240
5396
|
seedAgentInfo: (configDir, profileName, value) => seedAgentInfoNote(configDir, profileName, value, options.fetch),
|
|
5241
5397
|
connectionState: (profile) => agentConnectionState(profile, options.fetch),
|
|
5398
|
+
publishResumeNote: (configDir, profileName, runtime, sessionId) => publishResumeNote({
|
|
5399
|
+
configDir,
|
|
5400
|
+
profileName,
|
|
5401
|
+
runtime,
|
|
5402
|
+
sessionId,
|
|
5403
|
+
fetch: options.fetch
|
|
5404
|
+
}),
|
|
5242
5405
|
ompSession: ompProcessSession,
|
|
5243
5406
|
...DISCONNECT_DEFAULTS
|
|
5244
5407
|
}, options.dependencies);
|
|
@@ -5313,6 +5476,12 @@ async function registerSession(options) {
|
|
|
5313
5476
|
status: "ended",
|
|
5314
5477
|
detail: ENDED_DEFERRED_DETAIL
|
|
5315
5478
|
};
|
|
5479
|
+
if (current.disconnect === "kept") return {
|
|
5480
|
+
...known,
|
|
5481
|
+
address: current.address,
|
|
5482
|
+
status: "ended",
|
|
5483
|
+
detail: ENDED_KEPT_DETAIL
|
|
5484
|
+
};
|
|
5316
5485
|
const outcome = current.disconnect === "done" && !enrolledNow ? "done" : await finishSessionDisconnect(options.configDir, current, deps, env, options.fetch, enrolledNow);
|
|
5317
5486
|
if (outcome === "released") return {
|
|
5318
5487
|
...known,
|
|
@@ -5340,15 +5509,17 @@ async function registerSession(options) {
|
|
|
5340
5509
|
endedAt: null,
|
|
5341
5510
|
disconnect: null,
|
|
5342
5511
|
deferredUntil: null,
|
|
5512
|
+
revokeOnEnd: false,
|
|
5343
5513
|
endReason: null,
|
|
5344
5514
|
release: null
|
|
5345
5515
|
});
|
|
5346
5516
|
try {
|
|
5347
|
-
let record = await updateSessionRecord(options.configDir, sessionId, (current) => current?.endedAt && current.disconnect === "deferred" ? {
|
|
5517
|
+
let record = await updateSessionRecord(options.configDir, sessionId, (current) => current?.endedAt && (current.disconnect === "deferred" || current.disconnect === "kept") ? {
|
|
5348
5518
|
...current,
|
|
5349
5519
|
endedAt: null,
|
|
5350
5520
|
disconnect: null,
|
|
5351
5521
|
deferredUntil: null,
|
|
5522
|
+
revokeOnEnd: false,
|
|
5352
5523
|
endReason: null
|
|
5353
5524
|
} : current);
|
|
5354
5525
|
if (record?.endedAt) return await ended(record);
|
|
@@ -5361,6 +5532,14 @@ async function registerSession(options) {
|
|
|
5361
5532
|
agentInfo: seeded
|
|
5362
5533
|
} : current);
|
|
5363
5534
|
};
|
|
5535
|
+
const finishResumeNote = async () => {
|
|
5536
|
+
if (runtime === "omp") return {};
|
|
5537
|
+
try {
|
|
5538
|
+
return { resumeNote: await deps.publishResumeNote(options.configDir, profileName, runtime, sessionId) };
|
|
5539
|
+
} catch {
|
|
5540
|
+
return { resumeNote: "failed" };
|
|
5541
|
+
}
|
|
5542
|
+
};
|
|
5364
5543
|
const claudeHook = (agentAddress) => {
|
|
5365
5544
|
const outcome = deps.installClaudeHook({
|
|
5366
5545
|
cliPath: options.cliPath,
|
|
@@ -5387,7 +5566,7 @@ async function registerSession(options) {
|
|
|
5387
5566
|
if (state === "revoked" || state === "rejected") return {
|
|
5388
5567
|
...withAddress,
|
|
5389
5568
|
status: "revoked",
|
|
5390
|
-
detail:
|
|
5569
|
+
detail: `This session's agent was disconnected in Primitive, so it cannot receive mail. No new address was created. ${RECONNECT_HINT}`
|
|
5391
5570
|
};
|
|
5392
5571
|
if (state === "unavailable") return {
|
|
5393
5572
|
...withAddress,
|
|
@@ -5403,10 +5582,12 @@ async function registerSession(options) {
|
|
|
5403
5582
|
if (state === "connected") {
|
|
5404
5583
|
const hooked = installHook(existing);
|
|
5405
5584
|
await finishAgentInfo();
|
|
5585
|
+
const resumeNote = await finishResumeNote();
|
|
5406
5586
|
return {
|
|
5407
5587
|
...withAddress,
|
|
5408
5588
|
receiving: RECEIVING[runtime],
|
|
5409
5589
|
status: "already_registered",
|
|
5590
|
+
...resumeNote,
|
|
5410
5591
|
detail: hooked === "installed" ? "This session is already connected." : hooked === "held" ? HELD_DETAIL : "This session is already connected, but its receive hook could not be installed. Run `primitive machine doctor`."
|
|
5411
5592
|
};
|
|
5412
5593
|
}
|
|
@@ -5414,7 +5595,7 @@ async function registerSession(options) {
|
|
|
5414
5595
|
...known,
|
|
5415
5596
|
address: record.address,
|
|
5416
5597
|
status: "removed",
|
|
5417
|
-
detail:
|
|
5598
|
+
detail: `This session's local profile was removed. No new address was created for the same session. ${RECONNECT_HINT}`
|
|
5418
5599
|
};
|
|
5419
5600
|
let credentials;
|
|
5420
5601
|
try {
|
|
@@ -5474,7 +5655,11 @@ async function registerSession(options) {
|
|
|
5474
5655
|
const status = result.connection.status;
|
|
5475
5656
|
let hooked = "installed";
|
|
5476
5657
|
if (runtime === "claude" && submitted && status !== "owner_inactive") hooked = claudeHook(address);
|
|
5477
|
-
|
|
5658
|
+
let resumeNote = {};
|
|
5659
|
+
if (status === "connected") {
|
|
5660
|
+
await finishAgentInfo();
|
|
5661
|
+
resumeNote = await finishResumeNote();
|
|
5662
|
+
}
|
|
5478
5663
|
const withAddress = {
|
|
5479
5664
|
...known,
|
|
5480
5665
|
address,
|
|
@@ -5486,6 +5671,7 @@ async function registerSession(options) {
|
|
|
5486
5671
|
status: "registered",
|
|
5487
5672
|
name,
|
|
5488
5673
|
nameIsDefault: name === record.name,
|
|
5674
|
+
...resumeNote,
|
|
5489
5675
|
detail: hooked === "installed" ? `Connected as ${address}.` : hooked === "held" ? `Connected as ${address}. ${HELD_DETAIL}` : `Connected as ${address}, but the receive hook could not be installed. Run \`primitive machine doctor\`.`
|
|
5490
5676
|
};
|
|
5491
5677
|
if (status === "revoked" || status === "owner_inactive") return {
|
|
@@ -5512,24 +5698,62 @@ async function registerSession(options) {
|
|
|
5512
5698
|
}
|
|
5513
5699
|
}
|
|
5514
5700
|
/**
|
|
5515
|
-
* How long an end from a runtime hook waits before
|
|
5701
|
+
* How long an end from a runtime hook waits before it takes effect. A runtime
|
|
5516
5702
|
* reports the same end for a restart, a switch to another session and a real
|
|
5517
5703
|
* exit, so the hook cannot tell them apart; a start of the same session
|
|
5518
|
-
* within this window cancels the end
|
|
5704
|
+
* within this window cancels the end.
|
|
5519
5705
|
*/
|
|
5520
5706
|
const HOOK_END_GRACE_MS = 3e5;
|
|
5707
|
+
/**
|
|
5708
|
+
* How long an ended session keeps the agent `session-register` created for
|
|
5709
|
+
* it. A session that has not started again for this long has its agent
|
|
5710
|
+
* disconnected by `machine doctor --fix` or the next registration on this
|
|
5711
|
+
* machine, so closed sessions do not keep live credentials forever.
|
|
5712
|
+
*/
|
|
5713
|
+
const STALE_SESSION_MS = 2592e6;
|
|
5521
5714
|
/** The end of this record is a hook's, still waiting. */
|
|
5522
5715
|
function deferredEnd(record) {
|
|
5523
5716
|
return Boolean(record?.endedAt) && record?.disconnect === "deferred";
|
|
5524
5717
|
}
|
|
5718
|
+
/** Removes an ended session's own exact-session receive hooks. Best effort. */
|
|
5719
|
+
function removeSessionHooks(configDir, record, deps, env) {
|
|
5720
|
+
if (record.runtime !== "claude" || !record.address) return;
|
|
5721
|
+
try {
|
|
5722
|
+
deps.uninstallHook({
|
|
5723
|
+
configDir,
|
|
5724
|
+
profileName: record.profile,
|
|
5725
|
+
agentAddress: record.address,
|
|
5726
|
+
sessionId: record.session,
|
|
5727
|
+
env
|
|
5728
|
+
});
|
|
5729
|
+
} catch {}
|
|
5730
|
+
}
|
|
5525
5731
|
/**
|
|
5526
|
-
*
|
|
5527
|
-
*
|
|
5528
|
-
*
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5732
|
+
* The record of an end that keeps the agent. Its exact-session receive hooks
|
|
5733
|
+
* are removed in the same step, under the record's lock, so a start of the
|
|
5734
|
+
* same session that clears the end afterwards reinstalls them.
|
|
5735
|
+
*/
|
|
5736
|
+
function keptEnd(configDir, current, deps, env, at) {
|
|
5737
|
+
removeSessionHooks(configDir, current, deps, env);
|
|
5738
|
+
return {
|
|
5739
|
+
...current,
|
|
5740
|
+
endedAt: current.endedAt ?? at.toISOString(),
|
|
5741
|
+
disconnect: "kept",
|
|
5742
|
+
deferredUntil: null,
|
|
5743
|
+
revokeOnEnd: false
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5746
|
+
const KEPT_DETAIL = "This session is marked ended. Its agent stays connected, so resuming the session keeps the same address; it is disconnected only if the session does not start again for 30 days. Pass --revoke to disconnect it now.";
|
|
5747
|
+
/**
|
|
5748
|
+
* Record that a session ended. By default the agent `session-register`
|
|
5749
|
+
* created for it stays connected, with its credential and profile, so a
|
|
5750
|
+
* resume of the same session keeps its address; only its exact-session
|
|
5751
|
+
* receive hooks are removed. With `revoke` the agent is disconnected instead.
|
|
5752
|
+
* Agents connected any other way are left alone either way, including a
|
|
5753
|
+
* credential connected later into the same profile. The end is recorded
|
|
5754
|
+
* first, under the record's lock, so a registration still enrolling sees it.
|
|
5755
|
+
* With `graceMs` (an end reported by a runtime hook) the end waits that
|
|
5756
|
+
* long, and a start of the same session meanwhile cancels it. Never throws.
|
|
5533
5757
|
*/
|
|
5534
5758
|
async function endSession(options) {
|
|
5535
5759
|
const env = options.env ?? process.env;
|
|
@@ -5561,6 +5785,7 @@ async function endSession(options) {
|
|
|
5561
5785
|
};
|
|
5562
5786
|
const sessionId = session.toLowerCase();
|
|
5563
5787
|
const graceMs = Math.max(0, options.graceMs ?? 0);
|
|
5788
|
+
const revoke = options.revoke === true;
|
|
5564
5789
|
const reason = options.reason && /^[a-z_]{1,40}$/.test(options.reason) ? options.reason : null;
|
|
5565
5790
|
try {
|
|
5566
5791
|
let managed = false;
|
|
@@ -5574,21 +5799,29 @@ async function endSession(options) {
|
|
|
5574
5799
|
return current;
|
|
5575
5800
|
}
|
|
5576
5801
|
const now = deps.now();
|
|
5577
|
-
|
|
5802
|
+
const revoking = revoke || Boolean(current.endedAt) && (current.disconnect === "pending" || current.disconnect === "pending_enrollment" || current.disconnect === "deferred" && current.revokeOnEnd);
|
|
5803
|
+
if (!revoking && current.endedAt && current.disconnect === "kept") return current;
|
|
5804
|
+
if (graceMs > 0 && (!current.endedAt || deferredEnd(current) || current.disconnect === "kept")) {
|
|
5578
5805
|
deferred = true;
|
|
5579
5806
|
return {
|
|
5580
5807
|
...current,
|
|
5581
5808
|
endedAt: now.toISOString(),
|
|
5582
5809
|
disconnect: "deferred",
|
|
5583
5810
|
deferredUntil: new Date(now.getTime() + graceMs).toISOString(),
|
|
5811
|
+
revokeOnEnd: revoking,
|
|
5584
5812
|
endReason: reason
|
|
5585
5813
|
};
|
|
5586
5814
|
}
|
|
5815
|
+
if (!revoking) return {
|
|
5816
|
+
...keptEnd(options.configDir, current, deps, env, now),
|
|
5817
|
+
endReason: current.endReason ?? reason
|
|
5818
|
+
};
|
|
5587
5819
|
return {
|
|
5588
5820
|
...current,
|
|
5589
5821
|
endedAt: current.endedAt ?? now.toISOString(),
|
|
5590
5822
|
disconnect: current.disconnect === "pending_enrollment" ? "pending_enrollment" : "pending",
|
|
5591
5823
|
deferredUntil: null,
|
|
5824
|
+
revokeOnEnd: true,
|
|
5592
5825
|
endReason: current.endReason ?? reason
|
|
5593
5826
|
};
|
|
5594
5827
|
});
|
|
@@ -5616,19 +5849,25 @@ async function endSession(options) {
|
|
|
5616
5849
|
resumed = true;
|
|
5617
5850
|
return current;
|
|
5618
5851
|
}
|
|
5619
|
-
return {
|
|
5852
|
+
return current.revokeOnEnd ? {
|
|
5620
5853
|
...current,
|
|
5621
5854
|
disconnect: "pending",
|
|
5622
5855
|
deferredUntil: null
|
|
5623
|
-
};
|
|
5856
|
+
} : keptEnd(options.configDir, current, deps, env, deps.now());
|
|
5624
5857
|
});
|
|
5625
5858
|
if (resumed || !record) return {
|
|
5626
5859
|
...known,
|
|
5627
5860
|
status: "resumed",
|
|
5628
|
-
detail: "The session started again (or was ended again) before its end took effect, so this end
|
|
5861
|
+
detail: "The session started again (or was ended again) before its end took effect, so this end changed nothing."
|
|
5629
5862
|
};
|
|
5630
5863
|
}
|
|
5631
|
-
|
|
5864
|
+
if (record.disconnect === "kept") return {
|
|
5865
|
+
...known,
|
|
5866
|
+
status: "kept",
|
|
5867
|
+
detail: KEPT_DETAIL
|
|
5868
|
+
};
|
|
5869
|
+
const report = {};
|
|
5870
|
+
const outcome = await finishSessionDisconnect(options.configDir, record, deps, env, options.fetch, false, report);
|
|
5632
5871
|
if (outcome === "released") return {
|
|
5633
5872
|
...known,
|
|
5634
5873
|
status: "left_connected",
|
|
@@ -5637,6 +5876,7 @@ async function endSession(options) {
|
|
|
5637
5876
|
return outcome === "done" ? {
|
|
5638
5877
|
...known,
|
|
5639
5878
|
status: "disconnected",
|
|
5879
|
+
...report.resumeNote ? { resumeNote: report.resumeNote } : {},
|
|
5640
5880
|
detail: "This session's agent was disconnected."
|
|
5641
5881
|
} : {
|
|
5642
5882
|
...known,
|
|
@@ -5648,17 +5888,99 @@ async function endSession(options) {
|
|
|
5648
5888
|
...base,
|
|
5649
5889
|
session: sessionId,
|
|
5650
5890
|
status: "failed",
|
|
5651
|
-
detail: error instanceof AgentDisconnectError ? error.message : "
|
|
5891
|
+
detail: error instanceof AgentDisconnectError ? error.message : "The end did not finish. The agent was left as it was."
|
|
5652
5892
|
};
|
|
5653
5893
|
}
|
|
5654
5894
|
}
|
|
5655
5895
|
/**
|
|
5656
|
-
* Ended sessions whose agent disconnect
|
|
5657
|
-
*
|
|
5658
|
-
* it (for example because the
|
|
5896
|
+
* Ended sessions whose agent disconnect was asked for and still needs
|
|
5897
|
+
* confirming, including a `--revoke` end from a hook whose grace period is
|
|
5898
|
+
* over but whose own process never finished it (for example because the
|
|
5899
|
+
* machine shut down). Ends that keep their agent are never listed here.
|
|
5659
5900
|
*/
|
|
5660
5901
|
function pendingSessionDisconnects(configDir, now = /* @__PURE__ */ new Date()) {
|
|
5661
|
-
return listSessionRecords(configDir).filter((record) => record.createdBy === "session-register" && record.endedAt && (record.disconnect === "pending" || record.disconnect === "pending_enrollment" || record.disconnect === "deferred" && !(Date.parse(record.deferredUntil ?? "") > now.getTime())));
|
|
5902
|
+
return listSessionRecords(configDir).filter((record) => record.createdBy === "session-register" && record.endedAt && (record.disconnect === "pending" || record.disconnect === "pending_enrollment" || record.disconnect === "deferred" && record.revokeOnEnd && !(Date.parse(record.deferredUntil ?? "") > now.getTime())));
|
|
5903
|
+
}
|
|
5904
|
+
/**
|
|
5905
|
+
* Ended sessions that kept their agent and have not started again for
|
|
5906
|
+
* STALE_SESSION_MS, whose profile still holds exactly the credential
|
|
5907
|
+
* `session-register` created. Only these are disconnected for being stale.
|
|
5908
|
+
*/
|
|
5909
|
+
function staleSessionAgents(configDir, now = /* @__PURE__ */ new Date()) {
|
|
5910
|
+
return listSessionRecords(configDir).filter((record) => {
|
|
5911
|
+
if (record.createdBy !== "session-register" || !record.endedAt || !(record.disconnect === "kept" || record.disconnect === "deferred" && !record.revokeOnEnd) || !(now.getTime() - Date.parse(record.endedAt) >= 2592e6)) return false;
|
|
5912
|
+
try {
|
|
5913
|
+
const current = loadConnectedAgentProfile(configDir, record.profile);
|
|
5914
|
+
return Boolean(current && holdsRegisteredCredential(configDir, record, current));
|
|
5915
|
+
} catch {
|
|
5916
|
+
return false;
|
|
5917
|
+
}
|
|
5918
|
+
});
|
|
5919
|
+
}
|
|
5920
|
+
/**
|
|
5921
|
+
* Disconnect the agent of every stale ended session (see
|
|
5922
|
+
* staleSessionAgents). Each disconnect names the exact credential it
|
|
5923
|
+
* checked, under the locks `agent connect` also takes, so a credential
|
|
5924
|
+
* connected into the profile meanwhile is never revoked, and nothing is
|
|
5925
|
+
* ever revoked by address here. A start of the session meanwhile turns it
|
|
5926
|
+
* into an ordinary pending disconnect, which that start finishes. Returns
|
|
5927
|
+
* how many agents were disconnected.
|
|
5928
|
+
*/
|
|
5929
|
+
async function revokeStaleSessionAgents(configDir, options = {}) {
|
|
5930
|
+
const deps = withDefined(DISCONNECT_DEFAULTS, options.dependencies);
|
|
5931
|
+
const env = options.env ?? process.env;
|
|
5932
|
+
let revoked = 0;
|
|
5933
|
+
for (const listed of staleSessionAgents(configDir, deps.now())) {
|
|
5934
|
+
if (listed.session === options.exclude?.toLowerCase()) continue;
|
|
5935
|
+
try {
|
|
5936
|
+
const current = loadConnectedAgentProfile(configDir, listed.profile);
|
|
5937
|
+
if (!current || !holdsRegisteredCredential(configDir, listed, current)) continue;
|
|
5938
|
+
let claimed = false;
|
|
5939
|
+
await updateSessionRecord(configDir, listed.session, (row) => {
|
|
5940
|
+
if (row?.createdBy !== "session-register" || row.endedAt !== listed.endedAt || row.disconnect !== listed.disconnect) return row;
|
|
5941
|
+
claimed = true;
|
|
5942
|
+
return {
|
|
5943
|
+
...row,
|
|
5944
|
+
disconnect: "pending",
|
|
5945
|
+
deferredUntil: null,
|
|
5946
|
+
revokeOnEnd: true
|
|
5947
|
+
};
|
|
5948
|
+
});
|
|
5949
|
+
if (!claimed) continue;
|
|
5950
|
+
let outcome;
|
|
5951
|
+
try {
|
|
5952
|
+
await deps.disconnect({
|
|
5953
|
+
configDir,
|
|
5954
|
+
profileName: listed.profile,
|
|
5955
|
+
fetch: options.fetch,
|
|
5956
|
+
env,
|
|
5957
|
+
expected: {
|
|
5958
|
+
address: current.agent_address,
|
|
5959
|
+
invitationHash: current.invitation_hash
|
|
5960
|
+
}
|
|
5961
|
+
});
|
|
5962
|
+
outcome = "done";
|
|
5963
|
+
} catch (error) {
|
|
5964
|
+
outcome = error instanceof AgentCredentialChangedError ? "released" : "failed";
|
|
5965
|
+
}
|
|
5966
|
+
await updateSessionRecord(configDir, listed.session, (row) => {
|
|
5967
|
+
if (row?.createdBy !== "session-register" || row.disconnect !== "pending") return row;
|
|
5968
|
+
if (outcome === "done") return {
|
|
5969
|
+
...row,
|
|
5970
|
+
disconnect: "done"
|
|
5971
|
+
};
|
|
5972
|
+
if (outcome === "released") return releasedRecord(row, "credential_changed", deps.now());
|
|
5973
|
+
return {
|
|
5974
|
+
...row,
|
|
5975
|
+
disconnect: listed.disconnect,
|
|
5976
|
+
deferredUntil: listed.deferredUntil,
|
|
5977
|
+
revokeOnEnd: false
|
|
5978
|
+
};
|
|
5979
|
+
});
|
|
5980
|
+
if (outcome === "done") revoked++;
|
|
5981
|
+
} catch {}
|
|
5982
|
+
}
|
|
5983
|
+
return revoked;
|
|
5662
5984
|
}
|
|
5663
5985
|
/**
|
|
5664
5986
|
* Retry every pending disconnect. Each goes through the same credential check
|
|
@@ -5748,4 +6070,4 @@ async function runDetached(params) {
|
|
|
5748
6070
|
}
|
|
5749
6071
|
}
|
|
5750
6072
|
//#endregion
|
|
5751
|
-
export {
|
|
6073
|
+
export { installClaudeWakeHook as $, evaluateContactPolicy as A, contactAddress as B, RECEIVER_MODES as C, withOwnerMemberAddress as Ct, NETWORK_ADMISSION_PENDING_RETRY_MS as D, ContactPolicyReadRetryError as E, disconnectAgent as F, connectAgent as G, runContactRequest as H, AddressNotesApiError as I, refusalAfterReplacement as J, readAgentInvitation as K, addressNoteTarget as L, AgentDisconnectError as M, claimOverRevokedSetup as N, NetworkAdmissionPendingError as O, clearRevokedAddresses as P, releaseRegistrationsForProfile as Q, runAddressNotesRequest as R, runAgentConnect as S, refreshOwnerMemberAddressPeriodically as St, verificationReplySubmitted as T, agentConnectionStatus as U, contactAgentAddress as V, agentInvitationHash as W, savedReceiver as X, saveConnectionName as Y, MACHINE_RUNTIMES as Z, enrollmentResumes as _, jsonIndentation as _t, endSession as a, describeSessionHookItem as at, invitationProfileName as b, writeManagedFile as bt, pendingSessionDisconnects as c, findOnPath as ct, registerSession as d, readClaudeSettings as dt, recordHookMailCheck as et, retryPendingDisconnects as f, repairClaudeHooks as ft, enrollAgent as g, inspectManagedBlock as gt, staleSessionAgents as h, MachineFileError as ht, connectedProfilesForSession as i, describeSessionHookChange as it, canonicalContactSelector as j, createNotificationContactPolicy as k, psProcessInfo as l, hasMachineSessionHooks as lt, runDetached as m, writeClaudeSettings as mt, OMP_MAIN as n, claudeConfigDir as nt, headlessClaudeRun as o, ensureHookLauncher as ot, revokeStaleSessionAgents as p, samePath as pt, receiverStatusCommand as q, agentConnectionState as r, currentCliLocation as rt, machine_session_exports as s, ephemeralCliReason as st, HOOK_END_GRACE_MS as t, boundSessionProfiles as tt, readClaudeHookInput as u, inspectClaudeHooks as ut, connectWarnings as v, readManagedFile as vt, externalReceivingState as w, pollCheckCommand as x, readHookLauncherWarning as xt, defaultAgentProfileName as y, upsertManagedBlock as yt, ContactsApiError as z };
|