@agfpd/iapeer 0.2.54 → 0.2.56
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/daemon/daemon.test.ts +6 -1
- package/src/daemon/index.ts +2 -2
- package/src/launch/bootdeliver.test.ts +0 -1
- package/src/launch/index.ts +12 -8
- package/src/launch/launch.test.ts +0 -1
- package/src/launch/launchdRun.ts +0 -1
- package/src/launch/sockdir.test.ts +0 -1
- package/src/launch/types.ts +7 -6
- package/src/lifecycle/index.ts +5 -6
- package/src/lifecycle/lifecycle.test.ts +0 -2
package/package.json
CHANGED
|
@@ -77,7 +77,12 @@ describe('listTools', () => {
|
|
|
77
77
|
const tools = listTools(readPeersIndex()) as any[]
|
|
78
78
|
expect(tools.map(t => t.name).sort()).toEqual(['send_to_peer'])
|
|
79
79
|
expect(tools.every(t => t._meta['anthropic/alwaysLoad'] === true)).toBe(true)
|
|
80
|
-
|
|
80
|
+
const sendTool = tools.find(t => t.name === 'send_to_peer')
|
|
81
|
+
expect(sendTool.description).toContain('arthur')
|
|
82
|
+
expect(sendTool.description).toContain('wakes it and delivers there')
|
|
83
|
+
expect(sendTool.description).toContain('parallel to the default runtime')
|
|
84
|
+
expect(sendTool.inputSchema.properties.runtime.description).toContain('wakes it warm-on-demand')
|
|
85
|
+
expect(sendTool.inputSchema.properties.runtime.description).toContain('parallel session')
|
|
81
86
|
})
|
|
82
87
|
})
|
|
83
88
|
|
package/src/daemon/index.ts
CHANGED
|
@@ -98,7 +98,7 @@ export function buildSendDescription(index: PeersIndex): string {
|
|
|
98
98
|
}
|
|
99
99
|
lines.push(
|
|
100
100
|
'',
|
|
101
|
-
"Use `personality`, not a runtime-prefixed identity. Set `runtime`
|
|
101
|
+
"Use `personality`, not a runtime-prefixed identity. Set `runtime` to target a specific declared runtime for this send. If that runtime is warm-asleep, the daemon wakes it and delivers there; use this to intentionally bring up a second session of the same peer in parallel to the default runtime (for example codex alongside claude). Descriptions identify the peer's role; runtimes identify reachable delivery endpoints; intelligence identifies the nature of the peer (artificial / natural / absent). Inbound peer messages arrive as <iap from-personality=\"...\" from-runtime=\"...\" from-intelligence=\"...\"> blocks. The topic attribute is optional.",
|
|
102
102
|
)
|
|
103
103
|
return lines.join('\n')
|
|
104
104
|
}
|
|
@@ -122,7 +122,7 @@ export function listTools(index: PeersIndex): unknown[] {
|
|
|
122
122
|
runtime: {
|
|
123
123
|
type: 'string',
|
|
124
124
|
description:
|
|
125
|
-
'Optional
|
|
125
|
+
'Optional target runtime for this send. If the specified runtime is declared and wakeable but asleep, the daemon wakes it warm-on-demand and delivers there; choosing a non-default runtime can intentionally start that peer\'s parallel session. Runtime ids are short channel names such as claude, codex, telegram.',
|
|
126
126
|
pattern: RUNTIME_RE_SOURCE,
|
|
127
127
|
},
|
|
128
128
|
message: {
|
package/src/launch/index.ts
CHANGED
|
@@ -340,14 +340,18 @@ export const launch: LaunchFn = async (
|
|
|
340
340
|
const paneLog = `${cfg.logDir}/${identity}.log`
|
|
341
341
|
tmux(sock, 'pipe-pane', '-t', identity, `cat >> ${shellQuote(paneLog)}`)
|
|
342
342
|
|
|
343
|
-
// (4) Session self-TTL
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
343
|
+
// (4) [REMOVED 0.2.55] Session self-TTL. Was a tmux server-side
|
|
344
|
+
// `run-shell -b -d <maxAgeSecs>` → `kill-session`, intended to bound a zombie
|
|
345
|
+
// even if no supervise sweep runs. It was a BLIND wall-clock kill armed ONCE
|
|
346
|
+
// at birth and never re-armed, so it guillotined HEALTHY long-lived sessions
|
|
347
|
+
// at exactly birth+maxAge (4 h) — the recurrent boris/iapeer "mystery deaths".
|
|
348
|
+
// By construction any session that survives to maxAge is active: idle ones are
|
|
349
|
+
// already idle-reaped at idleSecs (1 h) by the activity-aware supervisor — so
|
|
350
|
+
// the blind kill could ONLY ever destroy live work (its sole "useful" case,
|
|
351
|
+
// daemon dead the whole interval, leaves an inert orphan nothing can deliver
|
|
352
|
+
// to, self-healed by idle-reap on daemon restart). The activity-aware idle-reap
|
|
353
|
+
// (superviseTick) is now the SOLE lifecycle bound. See ADR «миграция …» / vault
|
|
354
|
+
// note «Четырёхчасовой TTL-backstop …». (alwaysOn infra never armed it.)
|
|
351
355
|
|
|
352
356
|
// (5) Router runtime (telegram): no TUI input surface — there is no boot dialog
|
|
353
357
|
// and no ready marker to gate on. The process is up; return READY.
|
package/src/launch/launchdRun.ts
CHANGED
|
@@ -125,7 +125,6 @@ export async function runAlwaysOn(personality: string, runtime: string, cwd: str
|
|
|
125
125
|
sockDir,
|
|
126
126
|
bootDeadlineSecs: 30,
|
|
127
127
|
readyGateSecs: 30,
|
|
128
|
-
maxAgeSecs: 0, // unused — alwaysOn skips the self-TTL
|
|
129
128
|
// GLOBAL infra logs (Фаза §8): ~/.iapeer/logs/<personality>/ — match the plist's
|
|
130
129
|
// stdout/stderr dir (installAlwaysOnPlist), not per-peer <cwd>/.iapeer/logs/.
|
|
131
130
|
logDir: peerLogsDir(personality, { env }),
|
package/src/launch/types.ts
CHANGED
|
@@ -296,7 +296,6 @@ export interface LaunchConfig extends LaunchAdapterConfig {
|
|
|
296
296
|
sockDir: string
|
|
297
297
|
bootDeadlineSecs: number
|
|
298
298
|
readyGateSecs: number
|
|
299
|
-
maxAgeSecs: number
|
|
300
299
|
/** Log dir for pipe-pane output. */
|
|
301
300
|
logDir: string
|
|
302
301
|
/**
|
|
@@ -312,10 +311,12 @@ export interface LaunchConfig extends LaunchAdapterConfig {
|
|
|
312
311
|
exitLogDir?: string
|
|
313
312
|
env?: NodeJS.ProcessEnv
|
|
314
313
|
/**
|
|
315
|
-
* Always-on bring-up (infra runtimes held by launchd KeepAlive)
|
|
316
|
-
* session
|
|
317
|
-
*
|
|
318
|
-
*
|
|
314
|
+
* Always-on bring-up (infra runtimes held by launchd KeepAlive). Marks the
|
|
315
|
+
* session as infra-owned: launchd (KeepAlive) owns its lifecycle, the daemon is
|
|
316
|
+
* READ-ONLY for it (H4), and it opts out of the warm-on-demand `-v` tmux
|
|
317
|
+
* command-log (a rare-relaunch infra session has no rotation point and is not the
|
|
318
|
+
* dying warm class). Historically it also skipped the session self-TTL; that TTL
|
|
319
|
+
* was removed in 0.2.55 (see launch step 4), so the exemption is now moot.
|
|
319
320
|
*/
|
|
320
321
|
alwaysOn?: boolean
|
|
321
322
|
}
|
|
@@ -329,7 +330,7 @@ export interface LaunchResult {
|
|
|
329
330
|
|
|
330
331
|
/**
|
|
331
332
|
* Bring up ONE session: pre-clean stale tmux server → tmux new-session -d with
|
|
332
|
-
* adapter.buildArgv → pipe-pane →
|
|
333
|
+
* adapter.buildArgv → pipe-pane → boot (answer dialogs via
|
|
333
334
|
* adapter, wait for adapter.isInputReady, deliver the first message via
|
|
334
335
|
* load-buffer + bracketed paste — the same byte-path as warm delivery) →
|
|
335
336
|
* ready-gate (adapter.newestActivityMtime strictly advances).
|
package/src/lifecycle/index.ts
CHANGED
|
@@ -68,7 +68,6 @@ export interface LifecycleConfig {
|
|
|
68
68
|
bootDeadlineSecs: number
|
|
69
69
|
readyGateSecs: number
|
|
70
70
|
idleSecs: number
|
|
71
|
-
maxAgeSecs: number
|
|
72
71
|
/** Crash-loop guard: refuse to (re)launch after this many deaths within the window. */
|
|
73
72
|
crashLoopMax: number
|
|
74
73
|
/** Crash-loop guard: the sliding window (seconds) the death count is measured over. */
|
|
@@ -109,7 +108,6 @@ export function loadLifecycleConfig(env: NodeJS.ProcessEnv = process.env): Lifec
|
|
|
109
108
|
bootDeadlineSecs: num(env.IAPEER_BOOT_DEADLINE_SECS, 240),
|
|
110
109
|
readyGateSecs: num(env.IAPEER_READY_GATE_SECS, 120),
|
|
111
110
|
idleSecs: num(env.IAPEER_IDLE_SECS, 3600),
|
|
112
|
-
maxAgeSecs: num(env.IAPEER_MAX_AGE_SECS, 14400),
|
|
113
111
|
crashLoopMax: num(env.IAPEER_CRASHLOOP_MAX, 3),
|
|
114
112
|
crashLoopWindowSecs: num(env.IAPEER_CRASHLOOP_WINDOW_SECS, 300),
|
|
115
113
|
ephemeralQuietSecs: num(env.IAPEER_EPHEMERAL_QUIET_SECS, 20),
|
|
@@ -988,7 +986,6 @@ export async function wakeOrSpawn(args: WakeArgs, deps: WakeDeps = {}): Promise<
|
|
|
988
986
|
sockDir: cfg.sockDir,
|
|
989
987
|
bootDeadlineSecs: cfg.bootDeadlineSecs,
|
|
990
988
|
readyGateSecs: cfg.readyGateSecs,
|
|
991
|
-
maxAgeSecs: cfg.maxAgeSecs,
|
|
992
989
|
logDir: cfg.logDir,
|
|
993
990
|
// Exit-cause log → next to lifecycle.log (~/.iapeer/logs/iapeer), where the
|
|
994
991
|
// investigator already looks: a self-death now leaves `exits.log` with the
|
|
@@ -1007,13 +1004,15 @@ export async function wakeOrSpawn(args: WakeArgs, deps: WakeDeps = {}): Promise<
|
|
|
1007
1004
|
return { status: 'FAILED', woke: false, runtime, reason: result.reason }
|
|
1008
1005
|
}
|
|
1009
1006
|
// The session is up and the message delivered. Recording supervise-state must not
|
|
1010
|
-
// turn a successful wake into a failure (audit #18):
|
|
1011
|
-
//
|
|
1007
|
+
// turn a successful wake into a failure (audit #18): log loudly rather than throw
|
|
1008
|
+
// past a live spawn. NB since 0.2.55 (self-TTL removed) the session-state file IS
|
|
1009
|
+
// the only thing that enrolls this session into the activity-aware idle-reap — a
|
|
1010
|
+
// failed write leaves it UNSUPERVISED with no TTL backstop, hence the loud WARN.
|
|
1012
1011
|
try {
|
|
1013
1012
|
writeSessionState(cfg, { identity, runtime, personality: args.personality, cwd, wokeAt: Date.now() })
|
|
1014
1013
|
} catch (e) {
|
|
1015
1014
|
process.stderr.write(
|
|
1016
|
-
`[iapeer] WARN session-state write failed for ${identity} — session is live
|
|
1015
|
+
`[iapeer] WARN session-state write failed for ${identity} — session is live but UNSUPERVISED (not enrolled in idle-reap; self-TTL removed): ${e instanceof Error ? e.message : String(e)}\n`,
|
|
1017
1016
|
)
|
|
1018
1017
|
}
|
|
1019
1018
|
// A NEWLY launched session starts UNARMED by definition (no outbound yet) — clear
|
|
@@ -129,7 +129,6 @@ describe('withWakeLock', () => {
|
|
|
129
129
|
bootDeadlineSecs: 1,
|
|
130
130
|
readyGateSecs: 1,
|
|
131
131
|
idleSecs: 1,
|
|
132
|
-
maxAgeSecs: 1,
|
|
133
132
|
}) as LifecycleConfig
|
|
134
133
|
|
|
135
134
|
test('two concurrent locks on the same identity run strictly serialized', async () => {
|
|
@@ -194,7 +193,6 @@ describe('superviseTick H4 guard', () => {
|
|
|
194
193
|
bootDeadlineSecs: 1,
|
|
195
194
|
readyGateSecs: 1,
|
|
196
195
|
idleSecs: 1,
|
|
197
|
-
maxAgeSecs: 1,
|
|
198
196
|
}) as LifecycleConfig
|
|
199
197
|
|
|
200
198
|
function writeState(personality: string): string {
|