@agfpd/iapeer 0.4.88 → 0.4.89
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/package.json +1 -1
- package/src/identity/index.ts +55 -5
package/package.json
CHANGED
package/src/identity/index.ts
CHANGED
|
@@ -213,6 +213,41 @@ export function discoverPeerRuntimes(cwd: string, currentRuntime: Runtime): Runt
|
|
|
213
213
|
return uniqueRuntimes(discovered)
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
/** Agentic runtimes only an ARTIFICIAL peer can drive. A natural (human) or absent
|
|
217
|
+
* (no mind) peer has nobody to sit in a claude/codex session under its identity. */
|
|
218
|
+
const AGENTIC_GATED_RUNTIMES: ReadonlySet<string> = new Set(['claude', 'codex'])
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* DISCOVERY GATE (хвост-2, boris 15.07): drop agentic runtimes a NON-artificial peer
|
|
222
|
+
* would otherwise pick up from folder artifacts — a stray `.claude`/`.codex` dir or a
|
|
223
|
+
* leftover `.iapeer/<rt>` runtime scope in the cwd is NOT a declaration (live leak: a
|
|
224
|
+
* long-forgotten .claude/ in the human peer arthur's cwd made every create-прогон
|
|
225
|
+
* attach `claude` to his profile). The gate is by INTELLIGENCE, so it is robust to the
|
|
226
|
+
* artifacts' PRESENCE — the folder stays, the runtime is simply not attached. It also
|
|
227
|
+
* HEALS an already-leaked profile: applied to the merged set on re-provision, it
|
|
228
|
+
* scrubs previously-attached agentic runtimes off a natural/absent peer (with a warn).
|
|
229
|
+
* The EXPLICIT `options.runtime` always passes — an operator's deliberate command is
|
|
230
|
+
* not discovery (and explicit nature/runtime mismatches fail loud elsewhere:
|
|
231
|
+
* isIntelligenceAllowedForRuntime).
|
|
232
|
+
*/
|
|
233
|
+
export function gateRuntimesForIntelligence(
|
|
234
|
+
runtimes: Runtime[],
|
|
235
|
+
intelligence: Intelligence,
|
|
236
|
+
explicitRuntime: Runtime,
|
|
237
|
+
warn?: (message: string) => void,
|
|
238
|
+
): Runtime[] {
|
|
239
|
+
if (intelligence === 'artificial') return runtimes
|
|
240
|
+
const isGated = (rt: Runtime): boolean => AGENTIC_GATED_RUNTIMES.has(rt) && rt !== explicitRuntime
|
|
241
|
+
const dropped = runtimes.filter(isGated)
|
|
242
|
+
if (dropped.length === 0) return runtimes
|
|
243
|
+
warn?.(
|
|
244
|
+
`discovery gate: dropped agentic runtime(s) [${dropped.join(', ')}] for "${intelligence}" peer — ` +
|
|
245
|
+
`a .claude/.codex folder or leftover runtime scope in the cwd is not a declaration; ` +
|
|
246
|
+
`agentic runtimes attach only to artificial peers`,
|
|
247
|
+
)
|
|
248
|
+
return runtimes.filter(rt => !isGated(rt))
|
|
249
|
+
}
|
|
250
|
+
|
|
216
251
|
export function readPeerProfile(cwd: string = process.cwd()): PeerProfile | null {
|
|
217
252
|
const path = peerProfilePath(cwd)
|
|
218
253
|
if (!existsSync(path)) return null
|
|
@@ -566,14 +601,17 @@ export function ensurePeerProfile(options: EnsurePeerProfileOptions): PeerProfil
|
|
|
566
601
|
`PEER_PERSONALITY "${options.env.PEER_PERSONALITY}" does not match the peer being initialized ("${personality}")`,
|
|
567
602
|
)
|
|
568
603
|
}
|
|
604
|
+
// Intelligence FIRST — the discovery gate below keys on it (a natural/absent
|
|
605
|
+
// peer must not be born with agentic runtimes picked up from folder artifacts).
|
|
606
|
+
const intelligence = resolvedIntelligence(options.runtime)
|
|
569
607
|
const profile: PeerProfile = {
|
|
570
608
|
personality,
|
|
571
609
|
runtime: options.runtime,
|
|
572
|
-
runtimes: discoveredRuntimes,
|
|
610
|
+
runtimes: gateRuntimesForIntelligence(discoveredRuntimes, intelligence, options.runtime, options.warn),
|
|
573
611
|
// В36 — the description lands in the LOCAL profile at birth (source of truth);
|
|
574
612
|
// the registry row is a projection of this file, not the other way around.
|
|
575
613
|
description: options.description?.trim() ?? '',
|
|
576
|
-
intelligence
|
|
614
|
+
intelligence,
|
|
577
615
|
}
|
|
578
616
|
ensureLocalRuntimeScopes(cwd, profile.runtimes)
|
|
579
617
|
// INFRA runtime → provision the always-on launchd plist that holds it live.
|
|
@@ -609,8 +647,6 @@ export function ensurePeerProfile(options: EnsurePeerProfileOptions): PeerProfil
|
|
|
609
647
|
`PEER_PERSONALITY must match ${IAPEER_DIR}/${PEER_PROFILE_FILE} personality "${existing.personality}", got "${options.env.PEER_PERSONALITY}"`,
|
|
610
648
|
)
|
|
611
649
|
}
|
|
612
|
-
const mergedRuntimes = uniqueRuntimes([...existing.runtimes, ...discoveredRuntimes])
|
|
613
|
-
ensureLocalRuntimeScopes(cwd, mergedRuntimes)
|
|
614
650
|
// RE-PROVISION parity with the new-profile branch: an EXISTING infra peer (a
|
|
615
651
|
// migration / re-provision — e.g. `iapeer create alice --runtime telegram` to move
|
|
616
652
|
// a live telegram human onto the foundation) must ALSO get its always-on plist
|
|
@@ -641,11 +677,25 @@ export function ensurePeerProfile(options: EnsurePeerProfileOptions): PeerProfil
|
|
|
641
677
|
// preserved). resolvedIntelligence validates the explicit against the runtime's allowed set.
|
|
642
678
|
intelligence = resolvedIntelligence(options.runtime)
|
|
643
679
|
}
|
|
680
|
+
// Merge AFTER the intelligence is final, and GATE the WHOLE merged set (not just the
|
|
681
|
+
// fresh discoveries): for a natural/absent peer this both blocks a new agentic pickup
|
|
682
|
+
// AND heals a previously-leaked one (the arthur claude-leak class) on the next
|
|
683
|
+
// re-provision — with the folder artifacts still in place.
|
|
684
|
+
const mergedRuntimes = gateRuntimesForIntelligence(
|
|
685
|
+
uniqueRuntimes([...existing.runtimes, ...discoveredRuntimes]),
|
|
686
|
+
intelligence,
|
|
687
|
+
options.runtime,
|
|
688
|
+
options.warn,
|
|
689
|
+
)
|
|
690
|
+
ensureLocalRuntimeScopes(cwd, mergedRuntimes)
|
|
644
691
|
// В36 — an explicit description on RE-provision updates the local profile too (H1 in
|
|
645
692
|
// writePeerProfileAtomic still guards: absent/empty never wipes an existing value).
|
|
646
693
|
const explicitDescription = options.description?.trim()
|
|
647
694
|
const descriptionChanged = Boolean(explicitDescription) && explicitDescription !== existing.description
|
|
648
|
-
|
|
695
|
+
// CONTENT comparison, not length: the gate can DROP a leaked runtime while discovery
|
|
696
|
+
// ADDS a new one in the same pass (arthur: [telegram, claude] + web → [telegram, web])
|
|
697
|
+
// — equal lengths, different sets; a length check would silently skip the heal-write.
|
|
698
|
+
if (mergedRuntimes.join(',') !== existing.runtimes.join(',') || intelligence !== existing.intelligence || descriptionChanged) {
|
|
649
699
|
const updated = {
|
|
650
700
|
...existing,
|
|
651
701
|
runtimes: mergedRuntimes,
|