@idleware/ringi 0.6.1 → 0.7.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 +7 -18
- package/package.json +1 -1
- package/src/main/resources/profiles/a2a-1.json +0 -4
- package/src/main/resources/skill/ringi/SKILL.md +2 -18
- package/target/dts/adapters/host-posix/index.d.ts +2 -2
- package/target/dts/adapters/transport-git/branches.d.ts +2 -3
- package/target/dts/adapters/transport-git/index.d.ts +0 -1
- package/target/dts/adapters/transport-git/remote/types.d.ts +0 -1
- package/target/dts/adapters/transport-git/repo.d.ts +0 -1
- package/target/dts/clients/cli/agent/index.d.ts +0 -2
- package/target/dts/clients/cli/shared/index.d.ts +0 -1
- package/target/dts/core/agents/heartbeat/index.d.ts +2 -5
- package/target/dts/core/agents/heartbeat/pulse.d.ts +1 -3
- package/target/dts/core/agents/overview.d.ts +1 -5
- package/target/dts/core/agents/ports/host.d.ts +5 -37
- package/target/dts/core/agents/sessions/agent.d.ts +0 -17
- package/target/dts/core/agents/sessions/index.d.ts +0 -1
- package/target/dts/core/hub/history/index.d.ts +1 -11
- package/target/dts/core/hub/ports/store.d.ts +1 -5
- package/target/dts/core/hub/protocol/model.d.ts +0 -1
- package/target/dts/core/hub/protocol/reading.d.ts +0 -1
- package/target/dts/core/hub/rooms/index.d.ts +0 -4
- package/target/dts/core/hub/rooms/messages.d.ts +0 -1
- package/target/dts/core/hub/rooms/rooms.d.ts +1 -1
- package/target/dts/core/hub/signals/model.d.ts +0 -2
- package/target/dts/core/hub/usecases/hubs.d.ts +0 -1
- package/target/dts/core/hub/usecases/room.d.ts +0 -1
- package/target/dts/core/hub/usecases/rooms.d.ts +1 -5
- package/target/dts/core/hub/usecases/send.d.ts +0 -1
- package/target/dts/core/hub/usecases/view.d.ts +0 -22
- package/target/dts/core/hub/work/index.d.ts +0 -1
- package/target/dts/index.d.ts +0 -1
- package/target/esm/adapters/host-posix/index.js +30 -32
- package/target/esm/adapters/transport-git/branches.js +13 -20
- package/target/esm/adapters/transport-git/index.js +1 -6
- package/target/esm/adapters/transport-git/marks.js +1 -1
- package/target/esm/adapters/transport-git/remote/admit.js +0 -2
- package/target/esm/adapters/transport-git/remote/heartbeat-push.js +1 -1
- package/target/esm/adapters/transport-git/remote/main.js +1 -4
- package/target/esm/adapters/transport-git/repo.js +0 -1
- package/target/esm/clients/cli/agent/index.js +4 -10
- package/target/esm/clients/cli/arg-parse.js +1 -1
- package/target/esm/clients/cli/commands.js +6 -7
- package/target/esm/clients/cli/help.js +6 -13
- package/target/esm/clients/cli/operator/heartbeat-format.js +1 -1
- package/target/esm/clients/cli/operator/hub-commands.js +1 -1
- package/target/esm/clients/cli/operator/index.js +1 -1
- package/target/esm/clients/cli/operator/room.js +46 -7
- package/target/esm/clients/cli/operator/where-format.js +1 -3
- package/target/esm/clients/cli/shared/index.js +1 -2
- package/target/esm/clients/cli/shared/messages.js +1 -1
- package/target/esm/clients/cli/shared/profile.js +2 -2
- package/target/esm/clients/cli/shared/work.js +1 -1
- package/target/esm/composition/index.js +1 -3
- package/target/esm/core/agents/heartbeat/index.js +16 -23
- package/target/esm/core/agents/heartbeat/pulse.js +18 -33
- package/target/esm/core/agents/heartbeat/tools.js +1 -2
- package/target/esm/core/agents/overview.js +5 -6
- package/target/esm/core/agents/ports/host.js +2 -2
- package/target/esm/core/agents/sessions/agent.js +3 -23
- package/target/esm/core/agents/sessions/binding.js +5 -7
- package/target/esm/core/agents/sessions/index.js +3 -7
- package/target/esm/core/agents/sessions/join.js +1 -5
- package/target/esm/core/agents/sessions/resume.js +3 -4
- package/target/esm/core/agents/sessions/wait.js +10 -31
- package/target/esm/core/hub/capabilities.js +4 -2
- package/target/esm/core/hub/history/index.js +3 -1
- package/target/esm/core/hub/ports/store.js +3 -3
- package/target/esm/core/hub/protocol/index.js +3 -3
- package/target/esm/core/hub/protocol/model.js +2 -3
- package/target/esm/core/hub/protocol/reading.js +1 -3
- package/target/esm/core/hub/rooms/compat.js +1 -2
- package/target/esm/core/hub/rooms/hubs.js +2 -5
- package/target/esm/core/hub/rooms/index.js +3 -3
- package/target/esm/core/hub/rooms/messages.js +5 -10
- package/target/esm/core/hub/rooms/rooms.js +9 -38
- package/target/esm/core/hub/signals/effective.js +1 -21
- package/target/esm/core/hub/signals/index.js +2 -2
- package/target/esm/core/hub/signals/pins.js +1 -1
- package/target/esm/core/hub/usecases/hubs.js +0 -3
- package/target/esm/core/hub/usecases/room.js +2 -3
- package/target/esm/core/hub/usecases/rooms.js +4 -17
- package/target/esm/core/hub/usecases/roster.js +1 -1
- package/target/esm/core/hub/usecases/send.js +2 -12
- package/target/esm/core/hub/usecases/view.js +2 -6
- package/target/esm/core/hub/work/task.js +4 -11
- package/target/esm/index.js +1 -2
- package/target/esm/lib/json.js +1 -1
- package/target/dts/adapters/host-posix/group-state.d.ts +0 -7
- package/target/dts/adapters/host-posix/groups.d.ts +0 -1
- package/target/dts/adapters/host-posix/launch.d.ts +0 -7
- package/target/dts/adapters/host-posix/probes.d.ts +0 -5
- package/target/dts/adapters/host-posix/stats.d.ts +0 -9
- package/target/dts/adapters/transport-kafka/index.d.ts +0 -2
- package/target/dts/adapters/transport-temporal/index.d.ts +0 -2
- package/target/dts/clients/cli/operator/room-lifecycle.d.ts +0 -5
- package/target/dts/clients/cli/operator/room-list.d.ts +0 -5
- package/target/dts/clients/cli/operator/room-members.d.ts +0 -4
- package/target/dts/clients/cli/operator/watch.d.ts +0 -2
- package/target/dts/clients/cli/shared/budget.d.ts +0 -7
- package/target/dts/core/agents/turns/budgets.d.ts +0 -5
- package/target/dts/core/agents/turns/dispatch.d.ts +0 -9
- package/target/dts/core/agents/turns/exhaustion.d.ts +0 -11
- package/target/dts/core/agents/turns/grants.d.ts +0 -4
- package/target/dts/core/agents/turns/index.d.ts +0 -4
- package/target/dts/core/agents/turns/limits.d.ts +0 -2
- package/target/dts/core/agents/turns/messages.d.ts +0 -10
- package/target/dts/core/agents/turns/pings.d.ts +0 -5
- package/target/dts/core/agents/turns/plan.d.ts +0 -3
- package/target/dts/core/agents/turns/poll.d.ts +0 -8
- package/target/dts/core/agents/turns/results.d.ts +0 -3
- package/target/dts/core/agents/turns/seat.d.ts +0 -2
- package/target/dts/core/agents/turns/types.d.ts +0 -59
- package/target/dts/core/hub/decisions/budget.d.ts +0 -15
- package/target/dts/core/hub/decisions/check.d.ts +0 -6
- package/target/dts/core/hub/decisions/index.d.ts +0 -5
- package/target/dts/core/hub/decisions/proposal.d.ts +0 -38
- package/target/dts/core/hub/decisions/usage.d.ts +0 -12
- package/target/dts/core/hub/usecases/budget.d.ts +0 -23
- package/target/dts/core/hub/usecases/propose.d.ts +0 -17
- package/target/esm/adapters/host-posix/group-state.js +0 -24
- package/target/esm/adapters/host-posix/groups.js +0 -43
- package/target/esm/adapters/host-posix/launch.js +0 -80
- package/target/esm/adapters/host-posix/probes.js +0 -66
- package/target/esm/adapters/host-posix/stats.js +0 -27
- package/target/esm/adapters/transport-kafka/index.js +0 -4
- package/target/esm/adapters/transport-temporal/index.js +0 -4
- package/target/esm/clients/cli/operator/room-lifecycle.js +0 -23
- package/target/esm/clients/cli/operator/room-list.js +0 -27
- package/target/esm/clients/cli/operator/room-members.js +0 -15
- package/target/esm/clients/cli/operator/watch.js +0 -15
- package/target/esm/clients/cli/shared/budget.js +0 -50
- package/target/esm/core/agents/turns/budgets.js +0 -48
- package/target/esm/core/agents/turns/dispatch.js +0 -40
- package/target/esm/core/agents/turns/exhaustion.js +0 -16
- package/target/esm/core/agents/turns/grants.js +0 -36
- package/target/esm/core/agents/turns/index.js +0 -33
- package/target/esm/core/agents/turns/limits.js +0 -11
- package/target/esm/core/agents/turns/messages.js +0 -32
- package/target/esm/core/agents/turns/pings.js +0 -30
- package/target/esm/core/agents/turns/plan.js +0 -29
- package/target/esm/core/agents/turns/poll.js +0 -48
- package/target/esm/core/agents/turns/results.js +0 -40
- package/target/esm/core/agents/turns/seat.js +0 -44
- package/target/esm/core/agents/turns/types.js +0 -3
- package/target/esm/core/hub/decisions/budget.js +0 -40
- package/target/esm/core/hub/decisions/check.js +0 -43
- package/target/esm/core/hub/decisions/index.js +0 -10
- package/target/esm/core/hub/decisions/proposal.js +0 -73
- package/target/esm/core/hub/decisions/usage.js +0 -39
- package/target/esm/core/hub/usecases/budget.js +0 -78
- package/target/esm/core/hub/usecases/propose.js +0 -39
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
// heartbeat: whether an agent is alive, and working or waiting, judged from what its machine shows without asking the
|
|
2
|
-
// model, so no tokens are spent on it. The facts: its process
|
|
3
|
-
//
|
|
2
|
+
// model, so no tokens are spent on it. The facts: whether its process lives (ProcessHost), when its tool last wrote the
|
|
3
|
+
// session's trace (tools.ts) and its last ringi command. What to do about a silent agent is the orchestrator's.
|
|
4
4
|
import { agentPorts } from '../ports/index.js';
|
|
5
5
|
import { toolOf, traceTime } from './tools.js';
|
|
6
|
-
// The facts of one session now
|
|
7
|
-
export const probe = (s, waits = null
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
export const tracedBy = (s) => toolOf(s)?.tool ?? (s.product === 'watch' ? 'watch' : 'none');
|
|
19
|
-
// working: its process tree used CPU since the previous look, or its trace or a ringi command is younger than
|
|
20
|
-
// `active` seconds; waiting: alive but neither, or kept waiting by the operator or its room; gone: its process is
|
|
21
|
-
// known to have ended.
|
|
22
|
-
export const judge = (cur, prev = null, { active = 120, next = 30 } = {}) => {
|
|
6
|
+
// The facts of one session now.
|
|
7
|
+
export const probe = (s, waits = null) => ({
|
|
8
|
+
at: Date.now() / 1000,
|
|
9
|
+
alive: s.pid && agentPorts().host.supported ? agentPorts().host.alive(s.pid) : null,
|
|
10
|
+
trace: traceTime(s),
|
|
11
|
+
seen: Date.parse(s.lastSeen) / 1000,
|
|
12
|
+
stopped: s.state === 'stopped', waits,
|
|
13
|
+
});
|
|
14
|
+
export const tracedBy = (s) => toolOf(s)?.tool ?? 'none';
|
|
15
|
+
// working: its trace or a ringi command is younger than `active` seconds; waiting: alive but quiet, or kept waiting by
|
|
16
|
+
// the operator or its room; gone: its process is known to have ended.
|
|
17
|
+
export const judge = (cur, { active = 120, next = 30 } = {}) => {
|
|
23
18
|
const quiet = Math.max(0, Math.round(cur.at - Math.max(cur.seen, cur.trace ?? 0)));
|
|
24
|
-
const base = { alive: cur.alive, quiet,
|
|
19
|
+
const base = { alive: cur.alive, quiet, next };
|
|
25
20
|
if (cur.alive === false)
|
|
26
21
|
return { ...base, state: 'gone', waits: null };
|
|
27
22
|
if (cur.stopped)
|
|
28
23
|
return { ...base, state: 'waiting', waits: 'stopped' };
|
|
29
24
|
if (cur.waits)
|
|
30
25
|
return { ...base, state: 'waiting', waits: cur.waits };
|
|
31
|
-
|
|
32
|
-
return spent || quiet < active ? { ...base, state: 'working', waits: null } : { ...base, state: 'waiting', waits: 'inbox' };
|
|
26
|
+
return quiet < active ? { ...base, state: 'working', waits: null } : { ...base, state: 'waiting', waits: 'inbox' };
|
|
33
27
|
};
|
|
34
28
|
// The text of a heartbeat: facts as trailers, no word of the work itself.
|
|
35
29
|
export const body = (b) => [
|
|
@@ -37,7 +31,6 @@ export const body = (b) => [
|
|
|
37
31
|
...(b.waits ? [`Waits: ${b.waits}`] : []),
|
|
38
32
|
`Alive: ${b.alive === null ? 'unknown' : b.alive ? 'yes' : 'no'}`,
|
|
39
33
|
`Quiet: ${b.quiet}`,
|
|
40
|
-
...(b.cpu === null ? [] : [`CPU: ${b.cpu.toFixed(1)}`]),
|
|
41
34
|
`Next: ${b.next}`,
|
|
42
35
|
].join('\n') + '\n';
|
|
43
36
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// heartbeat: the pulse of every session this machine holds, judged from its process and its tool's trace without the
|
|
2
2
|
// model (index.ts). It reads session records only: it never binds a session, renews its lease or marks anything read.
|
|
3
3
|
import * as HB from './index.js';
|
|
4
|
-
import { agentPorts } from '../ports/index.js';
|
|
5
4
|
import { hubPorts } from '../../hub/ports/index.js';
|
|
6
5
|
import { claim, release } from './singleton.js';
|
|
7
6
|
export { ensurePulse } from './singleton.js';
|
|
@@ -18,41 +17,27 @@ const roomWaits = (s) => {
|
|
|
18
17
|
return null;
|
|
19
18
|
}
|
|
20
19
|
};
|
|
21
|
-
//
|
|
22
|
-
export const heartbeats =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!R || R.genesis !== s.genesis || !H.store.heartbeat)
|
|
39
|
-
return [];
|
|
40
|
-
const f = HB.probe(s, start(R).started ? null : 'quorum', rows);
|
|
41
|
-
const b = HB.judge(f, prev.get(s.handle) ?? null, { next });
|
|
42
|
-
prev.set(s.handle, f);
|
|
43
|
-
H.store.heartbeat(R.s, s.agent, R.s.tip, HB.body(b));
|
|
44
|
-
return [{ session: s, beat: b }];
|
|
45
|
-
}
|
|
46
|
-
catch (e) {
|
|
47
|
-
return [{ session: s, error: e.message }];
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
};
|
|
20
|
+
// Every session here, judged now.
|
|
21
|
+
export const heartbeats = () => allSessions().map(s => ({ session: s, tool: HB.tracedBy(s), beat: HB.judge(HB.probe(s, roomWaits(s))) }));
|
|
22
|
+
const beatAll = (next) => allSessions().flatMap((s) => {
|
|
23
|
+
try {
|
|
24
|
+
const H = openHub(s.hub);
|
|
25
|
+
const R = openRoom(H, s.room, { quiet: true });
|
|
26
|
+
// A gone room: the session leaves it on its own; nothing to beat for.
|
|
27
|
+
if (!R || R.genesis !== s.genesis || !H.store.heartbeat)
|
|
28
|
+
return [];
|
|
29
|
+
const b = HB.judge(HB.probe(s, start(R).started ? null : 'quorum'), { next });
|
|
30
|
+
H.store.heartbeat(R.s, s.agent, R.s.tip, HB.body(b));
|
|
31
|
+
return [{ session: s, beat: b }];
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
return [{ session: s, error: e.message }];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
51
37
|
// Beats until no session is left twice in a row, or the home is gone; `once`: one round.
|
|
52
38
|
const beatLoop = async (every, once, log) => {
|
|
53
|
-
const prev = new Map();
|
|
54
39
|
for (let idle = 0; idle < 2;) {
|
|
55
|
-
const beats = beatAll(
|
|
40
|
+
const beats = beatAll(every);
|
|
56
41
|
for (const b of beats)
|
|
57
42
|
if (b.error)
|
|
58
43
|
log(`pulse: ${b.session.agent} in ${b.session.hub}/${b.session.room}: ${b.error}`);
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// tools: which agent tool a session runs in, and when that tool last wrote the session's trace, asked of the tools
|
|
2
2
|
// this machine knows (core/agents/ports/tools.ts). Its modification time is the session's own activity, unlike the CPU of an app
|
|
3
|
-
// process that hosts many sessions.
|
|
4
|
-
// its turn (index.ts).
|
|
3
|
+
// process that hosts many sessions.
|
|
5
4
|
import { agentPorts } from '../ports/index.js';
|
|
6
5
|
const traced = (s) => {
|
|
7
6
|
for (const tool of agentPorts().tools) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// overview: where things stand for the operator: the hub, the room, who is in, open work
|
|
1
|
+
// overview: where things stand for the operator: the hub, the room, who is in, open work, the latest
|
|
2
2
|
// messages, and new ones as they land; and the sessions of this machine.
|
|
3
|
-
import * as D from '../hub/decisions/index.js';
|
|
4
3
|
import { hubPorts, locked } from '../hub/ports/index.js';
|
|
5
4
|
import { HIDDEN, OPERATOR, openHub, room as roomOf } from '../hub/rooms/index.js';
|
|
6
5
|
import { fail } from '../../lib/index.js';
|
|
@@ -12,11 +11,11 @@ import { rooms } from '../hub/usecases/rooms.js';
|
|
|
12
11
|
const sessionView = (s) => ({ ...s, liveness: liveness(s) });
|
|
13
12
|
export const where = (H, name) => {
|
|
14
13
|
if (!name)
|
|
15
|
-
return { hub: H, room: null, sessions: [], work: [],
|
|
14
|
+
return { hub: H, room: null, sessions: [], work: [], all: new Map(), messages: [] };
|
|
16
15
|
const R = roomOf(H, name);
|
|
17
16
|
const { all } = linked(R, OPERATOR);
|
|
18
17
|
return { hub: H, room: R, sessions: allSessions().filter(s => s.hub === H.name && s.genesis === R.genesis).map(sessionView),
|
|
19
|
-
work: tasks({ R, me: OPERATOR }).views,
|
|
18
|
+
work: tasks({ R, me: OPERATOR }).views,
|
|
20
19
|
all, messages: ordered([...all.values()].filter(m => !(m.kind && HIDDEN.has(m.kind)))) };
|
|
21
20
|
};
|
|
22
21
|
// The visible messages that landed since `seen` (which it extends), read afresh from the store.
|
|
@@ -28,9 +27,9 @@ export const arrived = (H, R, seen) => {
|
|
|
28
27
|
return { all, messages };
|
|
29
28
|
};
|
|
30
29
|
// The hub's rooms, each with how many of its sessions on this machine are active.
|
|
31
|
-
export const roomsActive = (H
|
|
30
|
+
export const roomsActive = (H) => {
|
|
32
31
|
const ss = allSessions().filter(s => s.hub === H.name && liveness(s) === 'active');
|
|
33
|
-
return rooms(H
|
|
32
|
+
return rooms(H).map(v => ({ ...v, active: ss.filter(s => s.genesis === v.genesis).length }));
|
|
34
33
|
};
|
|
35
34
|
export const sessions = () => {
|
|
36
35
|
hubPorts().state.ensure();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// host:
|
|
2
|
-
//
|
|
1
|
+
// host: what the operating system says about this machine's processes, and a detached start, apart from any operating
|
|
2
|
+
// system or agent tool. ringi starts one process of its own (the pulse); agents' processes are their tools'.
|
|
3
3
|
export {};
|
|
4
4
|
//# sourceMappingURL=host.js.map
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
// agent: what an agent does in its room as itself: read its inbox (marking what it read), take a piece of work, show
|
|
2
|
-
// who it is,
|
|
3
|
-
import { HIDDEN,
|
|
2
|
+
// who it is, wait.
|
|
3
|
+
import { HIDDEN, start, withSizes } from '../../hub/rooms/index.js';
|
|
4
4
|
import * as Signals from '../../hub/signals/write.js';
|
|
5
5
|
import { fail } from '../../../lib/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { link, lookup, unread } from '../../hub/protocol/index.js';
|
|
7
7
|
import { linked, loadRoomUnlocked, loadState, readerLock, saveState } from '../../hub/history/index.js';
|
|
8
|
-
import { isRef } from '../../hub/usecases/room.js';
|
|
9
8
|
export { wait } from './wait.js';
|
|
10
9
|
export { bound } from './index.js';
|
|
11
|
-
export { watch } from '../turns/index.js';
|
|
12
10
|
// Whether the agent's room has its quorum and started its work; a room that has not is not read.
|
|
13
11
|
export const started = ({ R }) => start(R);
|
|
14
12
|
export const inbox = ({ R, me }, { peek = false, limit = 20 } = {}) => {
|
|
@@ -33,22 +31,4 @@ export const take = ({ R, me }, ref) => {
|
|
|
33
31
|
export const whoami = ({ s, R }) => ({
|
|
34
32
|
session: s ?? fail('agent identity needs a joined session'), profile: R.profile.name, members: Object.keys(R.members),
|
|
35
33
|
});
|
|
36
|
-
export const card = ({ R, me }, text) => {
|
|
37
|
-
const message = compose(text, [['To', '*'], ['Kind', 'card']]);
|
|
38
|
-
checkOutgoing(message, isRef(R));
|
|
39
|
-
return append(R, me, message);
|
|
40
|
-
};
|
|
41
|
-
export const ping = ({ R, me }, { to, text = 'ping: still working', next = 600, re }) => {
|
|
42
|
-
if (!ID_RE.test(to))
|
|
43
|
-
fail(`bad recipient ${to}`);
|
|
44
|
-
if (!Number.isFinite(next) || next <= 0)
|
|
45
|
-
fail('ping next must be positive seconds');
|
|
46
|
-
const { all } = linked(R, me);
|
|
47
|
-
const mine = [...all.values()].filter(m => m.from === me && m.kind === 'ping' && m.to.includes(to)).at(-1);
|
|
48
|
-
const pong = mine && [...all.values()].find(m => m.kind === 'pong' && m.from === to && m.reId === mine.id);
|
|
49
|
-
const parent = re ? lookup(all, re) : null;
|
|
50
|
-
const message = compose(text, [['To', to], ['Re', parent?.id], ['Kind', 'ping'], ['Next', String(next)], ['Hop', parent && String(parent.hopEff + 1)]]);
|
|
51
|
-
checkOutgoing(message, isRef(R));
|
|
52
|
-
return { id: append(R, me, message), to, previous: mine ? { id: mine.id, answeredAt: pong?.time ?? null } : null };
|
|
53
|
-
};
|
|
54
34
|
//# sourceMappingURL=agent.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// binding: the seat a chat holds, checked on every agent command: joined, not stopped, its room alive and the same
|
|
2
2
|
// room, still a member; a chat whose room is gone leaves it on its own.
|
|
3
3
|
import { locked } from '../../hub/ports/index.js';
|
|
4
|
-
import { openHub, openRoom
|
|
4
|
+
import { openHub, openRoom } from '../../hub/rooms/index.js';
|
|
5
5
|
import { fail, stamp } from '../../../lib/index.js';
|
|
6
6
|
import { dropSession, readSession, saveSession, whoAmI } from './index.js';
|
|
7
7
|
// The binding, checked on every agent command: joined, not stopped, room alive and the same room, still a member.
|
|
@@ -22,14 +22,12 @@ export const bound = () => {
|
|
|
22
22
|
};
|
|
23
23
|
const checkRoomIdentity = (me, s, H, R) => {
|
|
24
24
|
if (!R || R.genesis !== s.genesis) {
|
|
25
|
-
// The room's branch is gone (deleted,
|
|
26
|
-
//
|
|
25
|
+
// The room's branch is gone (deleted, or deleted and made anew): the session leaves it on its own. A hub that
|
|
26
|
+
// cannot be reached fails above and leaves nothing.
|
|
27
27
|
locked('sessions', () => { if (readSession(me.handle)?.genesis === s.genesis)
|
|
28
28
|
dropSession(me.handle); });
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
: R ? `room ${s.room} was deleted and created again; this session left the old room. The operator joins it anew`
|
|
32
|
-
: `room ${s.room} is gone from hub ${s.hub}; this session left it`);
|
|
29
|
+
fail(R ? `room ${s.room} was deleted and created again; this session left the old room. The operator joins it anew`
|
|
30
|
+
: `room ${s.room} is gone from hub ${s.hub}; this session left it`);
|
|
33
31
|
}
|
|
34
32
|
};
|
|
35
33
|
const renewSession = (me, s) => {
|
|
@@ -43,15 +43,11 @@ export const saveSession = (s) => hubPorts().state.write(sessionKey(s.handle), s
|
|
|
43
43
|
export const dropSession = (handle) => hubPorts().state.remove(sessionKey(handle));
|
|
44
44
|
export const pidAlive = (pid) => agentPorts().host.alive(pid);
|
|
45
45
|
// active: in use within the lease by a living process; stopped: paused by the operator; gone: neither. A gone or
|
|
46
|
-
// stopped binding keeps its seat and scope.
|
|
47
|
-
// lease.
|
|
46
|
+
// stopped binding keeps its seat and scope.
|
|
48
47
|
export const liveness = (s) => s.state === 'stopped' ? 'stopped'
|
|
49
|
-
: s.
|
|
50
|
-
: Date.now() - Date.parse(s.lastSeen) < LEASE && (!s.pid || pidAlive(s.pid)) ? 'active' : 'gone';
|
|
48
|
+
: Date.now() - Date.parse(s.lastSeen) < LEASE && (!s.pid || pidAlive(s.pid)) ? 'active' : 'gone';
|
|
51
49
|
export const seatHolder = (hub, genesis, agent, except) => allSessions().find(s => s.hub === hub && s.genesis === genesis && s.agent === agent && s.handle !== except);
|
|
52
|
-
|
|
53
|
-
export const ownSession = (handle) => readSession(handle)
|
|
54
|
-
?? allSessions().find(s => s.product === 'watch' && s.takenFrom === handle) ?? null;
|
|
50
|
+
export const ownSession = (handle) => readSession(handle);
|
|
55
51
|
export const parseSelector = (sel) => {
|
|
56
52
|
const m = /^(?:([a-z0-9._-]+):)?([^/]+)\/([^/]+)$/.exec(sel ?? '');
|
|
57
53
|
return m ? { hub: m[1] ?? null, room: m[2], agent: m[3] } : fail('a session is named <room>/<agent> or <hub>:<room>/<agent>');
|
|
@@ -3,15 +3,13 @@ import { locked } from '../../hub/ports/index.js';
|
|
|
3
3
|
import { OPERATOR, controlChange, openHub, room as roomOf } from '../../hub/rooms/index.js';
|
|
4
4
|
import { fail, note, stamp } from '../../../lib/index.js';
|
|
5
5
|
import { AGENT_RE } from '../../hub/protocol/index.js';
|
|
6
|
-
import { dropSession, liveness, ownSession,
|
|
6
|
+
import { dropSession, liveness, ownSession, readSession, saveSession, seatHolder, whoAmI } from './index.js';
|
|
7
7
|
export const join = ({ hub, room, agent: id, scope }) => {
|
|
8
8
|
const me = whoAmI();
|
|
9
9
|
const agent = id ?? me.product ?? fail('--as <id> is required: this session\'s product is unknown');
|
|
10
10
|
if (!AGENT_RE.test(agent) || agent === OPERATOR)
|
|
11
11
|
fail(`bad agent id ${agent}: lowercase letters, digits, - and _, no dot`);
|
|
12
12
|
const H = openHub(hub), R = roomOf(H, room);
|
|
13
|
-
if (R.archived)
|
|
14
|
-
fail(`${room} is archived`);
|
|
15
13
|
if (H.signed) {
|
|
16
14
|
if (!H.store.joinKey)
|
|
17
15
|
fail(`hub ${H.name}: its store cannot make keys`);
|
|
@@ -39,8 +37,6 @@ const previousSession = ({ me, H, R, agent, room }) => {
|
|
|
39
37
|
if (holder)
|
|
40
38
|
fail(`${room}/${agent} is held by another session (${holder.handle}, ${liveness(holder)}). A seat ends only with leave, or the operator's takeover: "continue ${room}/${agent}" in this chat`);
|
|
41
39
|
const mine = ownSession(me.handle);
|
|
42
|
-
if (mine?.product === 'watch' && mine.pid && pidAlive(mine.pid))
|
|
43
|
-
fail('this chat handed its seat to a live watcher; stop or leave it before joining another room');
|
|
44
40
|
if (mine && mine.handle !== me.handle)
|
|
45
41
|
dropSession(mine.handle);
|
|
46
42
|
return mine;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// resume: a chat continues its own seat after a restart, or takes over one the operator names.
|
|
2
2
|
import { hubPorts, locked } from '../../hub/ports/index.js';
|
|
3
3
|
import { fail, note, stamp } from '../../../lib/index.js';
|
|
4
|
-
import { allSessions, dropSession, liveness, ownSession, parseSelector,
|
|
4
|
+
import { allSessions, dropSession, liveness, ownSession, parseSelector, saveSession, whoAmI } from './index.js';
|
|
5
5
|
export const resume = (selector) => {
|
|
6
6
|
const me = whoAmI();
|
|
7
7
|
hubPorts().state.ensure();
|
|
@@ -37,7 +37,7 @@ const ownTarget = (me) => {
|
|
|
37
37
|
};
|
|
38
38
|
const takeOver = (me, target) => {
|
|
39
39
|
if (target.handle !== me.handle) {
|
|
40
|
-
if (liveness(target) === 'active'
|
|
40
|
+
if (liveness(target) === 'active')
|
|
41
41
|
fail(`${target.room}/${target.agent} is held by a live session (${target.handle}); it is not taken over`);
|
|
42
42
|
dropSession(target.handle);
|
|
43
43
|
note(`took over ${target.hub}/${target.room}/${target.agent} from ${target.handle}`);
|
|
@@ -46,8 +46,7 @@ const takeOver = (me, target) => {
|
|
|
46
46
|
const resumed = (me, target) => {
|
|
47
47
|
const session = {
|
|
48
48
|
...target, handle: me.handle, product: target.handle === me.handle ? target.product : me.product, state: 'active',
|
|
49
|
-
since: target.handle === me.handle ? target.since : stamp(), lastSeen: stamp(), pid: me.pid ?? (target.handle === me.handle ? target.pid : null)
|
|
50
|
-
takenFrom: target.handle === me.handle ? target.takenFrom : undefined
|
|
49
|
+
since: target.handle === me.handle ? target.since : stamp(), lastSeen: stamp(), pid: me.pid ?? (target.handle === me.handle ? target.pid : null)
|
|
51
50
|
};
|
|
52
51
|
return session;
|
|
53
52
|
};
|
|
@@ -1,47 +1,26 @@
|
|
|
1
|
-
// wait: an agent holds the line until something new arrives for it or
|
|
2
|
-
|
|
1
|
+
// wait: an agent holds the line until something new arrives for it, or at most `max` seconds (and never past 110, so
|
|
2
|
+
// a tool call that runs it is not cut off).
|
|
3
|
+
import { HIDDEN } from '../../hub/rooms/index.js';
|
|
3
4
|
import { fail, sleep } from '../../../lib/index.js';
|
|
4
|
-
import {
|
|
5
|
+
import { unread } from '../../hub/protocol/index.js';
|
|
5
6
|
import { loadRoom } from '../../hub/history/index.js';
|
|
6
7
|
import { bound } from './index.js';
|
|
7
8
|
import { inbox } from './agent.js';
|
|
8
9
|
export const wait = async (a, { max = 120, ...options } = {}) => {
|
|
9
10
|
if (!Number.isFinite(max) || max < 0)
|
|
10
11
|
fail('wait max must be nonnegative seconds');
|
|
11
|
-
const start = Date.now(),
|
|
12
|
-
let last = start, silence = Math.min(max, 110);
|
|
12
|
+
const start = Date.now(), until = start + Math.min(max, 110) * 1000;
|
|
13
13
|
for (;;) {
|
|
14
14
|
// Each poll renews the lease and checks stop, takeover, membership and the room's identity.
|
|
15
15
|
const current = bound();
|
|
16
16
|
if (current.R.genesis !== a.R.genesis || current.me !== a.me)
|
|
17
17
|
fail('the waiting session changed its binding');
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
if (unread(logs, me, st.seen ?? {}, Infinity, HIDDEN).msgs.length)
|
|
18
|
+
const { logs, st } = loadRoom(current.R, current.me);
|
|
19
|
+
if (unread(logs, current.me, st.seen ?? {}, Infinity, HIDDEN).msgs.length)
|
|
21
20
|
return { inbox: inbox(current, options), seconds: (Date.now() - start) / 1000 };
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
last = state.last;
|
|
26
|
-
silence = state.silence;
|
|
27
|
-
const now = Date.now();
|
|
28
|
-
if (now - last >= silence * 1000 || now - start >= max * 1000)
|
|
29
|
-
return { inbox: null, seconds: (now - start) / 1000 };
|
|
30
|
-
await sleep(Math.min(1000, max * 1000 - (now - start)));
|
|
21
|
+
if (Date.now() >= until)
|
|
22
|
+
return { inbox: null, seconds: (Date.now() - start) / 1000 };
|
|
23
|
+
await sleep(Math.min(1000, until - Date.now()));
|
|
31
24
|
}
|
|
32
25
|
};
|
|
33
|
-
const answerPings = (a, logs, state) => {
|
|
34
|
-
for (const m of logs.get(a.me) ?? [])
|
|
35
|
-
if (m.kind === 'pong' && m.reId)
|
|
36
|
-
state.ponged.add(m.reId);
|
|
37
|
-
for (const msgs of logs.values())
|
|
38
|
-
for (const m of msgs) {
|
|
39
|
-
if (m.kind !== 'ping' || m.bad || !m.to.includes(a.me) || state.ponged.has(m.id))
|
|
40
|
-
continue;
|
|
41
|
-
state.ponged.add(m.id);
|
|
42
|
-
state.last = Date.now();
|
|
43
|
-
state.silence = Math.max(state.silence, Number(m.trailers.next?.[0]) || 600);
|
|
44
|
-
append(a.R, a.me, pongText(a.me, m, 'is waiting'));
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
26
|
//# sourceMappingURL=wait.js.map
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// The capabilities this build enforces: `<name>/<major>` → the module whose folds apply it. A new major is a new module
|
|
2
2
|
// registered beside the old one. A hub contract selects at most one major of each name; a room applies what its
|
|
3
3
|
// contract base selects (docs/ARCHITECTURE.md, Evolution).
|
|
4
|
-
import * as decisions from './decisions/index.js';
|
|
5
4
|
import * as profiles from './work/index.js';
|
|
6
|
-
|
|
5
|
+
// decisions/1 (proposals, grants, budgets) is retired: nothing in this build creates, decides or spends what it
|
|
6
|
+
// governs, so a contract that still names it restricts nothing here and is read.
|
|
7
|
+
const RETIRED = {};
|
|
8
|
+
export const CAPABILITIES = { [profiles.PROFILES_CAPABILITY]: profiles, 'decisions/1': RETIRED };
|
|
7
9
|
// Why a contract's capability list cannot be enforced by this build, or nothing.
|
|
8
10
|
export const unenforceable = (ids) => {
|
|
9
11
|
const names = ids.map(id => id.split('/')[0]);
|
|
@@ -59,7 +59,9 @@ const acceptSignals = (R, logs, st, alarms) => {
|
|
|
59
59
|
// What follows from an alarm, by what it is about.
|
|
60
60
|
const remedy = (R, alarm) => {
|
|
61
61
|
if (alarm.startsWith('signal '))
|
|
62
|
-
return '
|
|
62
|
+
return alarm.endsWith('was removed after it was accepted')
|
|
63
|
+
? 'the messages stay the truth; its author restores it with ringi agent signals --repair'
|
|
64
|
+
: 'the messages stay the truth; the signal is ignored';
|
|
63
65
|
if (alarm.startsWith('commit '))
|
|
64
66
|
return 'it is skipped; the room reads on without it';
|
|
65
67
|
return `it is quarantined and withheld. Restore it, or the operator runs ringi forget ${R.name}/<log>`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
// store: the port every storage type implements, each an adapter (adapters/transport-<type
|
|
2
|
-
//
|
|
1
|
+
// store: the port every storage type implements, each an adapter (adapters/transport-<type>) that composition/
|
|
2
|
+
// registers. Rooms, accepted history, profiles and signals use only this
|
|
3
3
|
// interface; git is the first implementation, and a hub's descriptor names which one it uses (`store.type`). A store
|
|
4
|
-
// keeps the shared contract (versioned files the participants agree on: contract.json,
|
|
4
|
+
// keeps the shared contract (versioned files the participants agree on: contract.json, profiles), the
|
|
5
5
|
// rooms (a control record with versioned files, members and roster, and one append-only log per member) and signals
|
|
6
6
|
// (named markers on log entries); the port is split by what each part does (Log, Attachments, …).
|
|
7
7
|
// Revisions are opaque, store-assigned ids. A store says whether one revision extends another; ringi builds the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// protocol: the message protocol, docs/spec/core-1.md (§3, §5
|
|
2
|
-
// turns its records into messages and hands them here: the text (text.ts), threads and order (threads.ts), inboxes
|
|
3
|
-
//
|
|
1
|
+
// protocol: the message protocol, docs/spec/core-1.md (§3, §5, §6), independent of storage and of any task model. A store
|
|
2
|
+
// turns its records into messages and hands them here: the text (text.ts), threads and order (threads.ts), inboxes
|
|
3
|
+
// (reading.ts). What a task is, which states it has and who may move it, is a profile, not part of the protocol.
|
|
4
4
|
export * from './model.js';
|
|
5
5
|
export * from './text.js';
|
|
6
6
|
export * from './threads.js';
|
|
@@ -8,8 +8,7 @@ export const TRAILER_RE = /^([A-Za-z][A-Za-z0-9-]*):[ \t]*(.*)$/;
|
|
|
8
8
|
export const TEXT_MAX = 64 * 1024;
|
|
9
9
|
// Trailers the protocol itself interprets; any other trailer belongs to an extension or a profile.
|
|
10
10
|
export const KNOWN = new Set(['to', 're', 'kind', 'state', 'hop', 'assign', 'roles']);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export const HIDDEN = new Set(['ping', 'pong', 'usage', 'card', 'room']);
|
|
11
|
+
// Kinds that are bookkeeping, never delivered to an inbox: room records.
|
|
12
|
+
export const HIDDEN = new Set(['room']);
|
|
14
13
|
export const one = (m, k) => m.trailers[k]?.[0] ?? null;
|
|
15
14
|
//# sourceMappingURL=model.js.map
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
// reading: what is delivered to whom (core-1 §6)
|
|
1
|
+
// reading: what is delivered to whom (core-1 §6).
|
|
2
2
|
import { ordered } from './order.js';
|
|
3
3
|
const addressed = (m, id) => m.to.includes(id) || m.to.includes('*');
|
|
4
4
|
const hiddenKind = (m, hidden) => !!m.kind && hidden.has(m.kind);
|
|
5
5
|
// Delivered: well-formed, not a hidden kind, addressed to `id` by name or `*`.
|
|
6
6
|
export const visible = (m, id, hidden) => !m.invalid && !m.bad && !hiddenKind(m, hidden) && addressed(m, id);
|
|
7
|
-
// Wakes: only a direct address. Broadcasts are read when the agent is awake for another reason.
|
|
8
|
-
export const wakes = (m, id, hidden) => visible(m, id, hidden) && m.to.includes(id);
|
|
9
7
|
const after = (msgs, cursor) => msgs.slice(msgs.findIndex(m => m.id === cursor) + 1);
|
|
10
8
|
const others = (logs, id) => [...logs].filter(([log]) => log !== id);
|
|
11
9
|
// Each log's cursor moves past what was shown and what is not for `id`, up to the first message held back.
|
|
@@ -42,8 +42,7 @@ export const contractAt = (store, name, rev) => {
|
|
|
42
42
|
const why = refusal(c, name);
|
|
43
43
|
if (why)
|
|
44
44
|
fail(why);
|
|
45
|
-
|
|
46
|
-
return { ...c, authority: a === null ? {} : strictJson(a, `hub ${name}: authority.json at ${short(rev)}`) };
|
|
45
|
+
return c;
|
|
47
46
|
};
|
|
48
47
|
// Why this ringi cannot read the hub's store as it is laid out (`shown`: what its contract names, or what its store
|
|
49
48
|
// type finds there when the contract names nothing), or nothing; `reads` is what the store type reads.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// hubs: a hub's descriptor beside its store, the contract it carries, and opening it.
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import * as D from '../decisions/index.js';
|
|
5
4
|
import { DEFAULT_PROFILE, PROFILES_CAPABILITY } from '../work/index.js';
|
|
6
5
|
import { hubPorts } from '../ports/index.js';
|
|
7
6
|
import { fail } from '../../../lib/index.js';
|
|
@@ -15,17 +14,15 @@ const VERSION = () => hubPorts().build.version;
|
|
|
15
14
|
const HUB_MD = `# ringi hub
|
|
16
15
|
|
|
17
16
|
The shared contract of a ringi hub (docs/spec/hub-1.md): contract.json (schema, version, protocol, trust, capabilities, the
|
|
18
|
-
default profile)
|
|
19
|
-
profiles/ (what a piece of work is in a room: its states, and who may move it). Use \`ringi\`; never edit by hand.
|
|
17
|
+
default profile) and profiles/ (what a piece of work is in a room: its states, and who may move it). Use \`ringi\`; never edit by hand.
|
|
20
18
|
`;
|
|
21
19
|
export const readProfileResource = (name) => fs.readFileSync(path.join(hubPorts().build.resources, 'profiles', `${name}.json`), 'utf8');
|
|
22
20
|
// A new hub: local (asserted senders), or signed, with the operator's root key generated on this machine and pinned.
|
|
23
21
|
// The files of a new hub's first contract.
|
|
24
22
|
const genesisFiles = (d, layout) => {
|
|
25
|
-
const contract = { schema: 'ringi-hub', version: '1.2', protocol: PROTOCOL_MAJOR, trust: d.trust, store: layout, capabilities: [
|
|
23
|
+
const contract = { schema: 'ringi-hub', version: '1.2', protocol: PROTOCOL_MAJOR, trust: d.trust, store: layout, capabilities: [PROFILES_CAPABILITY], profile: DEFAULT_PROFILE, ...(d.root ? { root: d.root } : {}) };
|
|
26
24
|
return {
|
|
27
25
|
'contract.json': JSON.stringify(contract, null, 2) + '\n',
|
|
28
|
-
'authority.json': JSON.stringify({ decide: {} }, null, 2) + '\n',
|
|
29
26
|
'HUB.md': HUB_MD,
|
|
30
27
|
[`profiles/${DEFAULT_PROFILE}.json`]: readProfileResource(DEFAULT_PROFILE),
|
|
31
28
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// hub: hubs and rooms on top of any store. A hub is a directory with a descriptor (hub.json: which store, where,
|
|
2
|
-
// what this machine pins) beside its store. Its shared contract lives in the store (contract.json,
|
|
3
|
-
//
|
|
2
|
+
// what this machine pins) beside its store. Its shared contract lives in the store (contract.json, profiles/*.json).
|
|
3
|
+
// hubs.ts reads descriptors and contracts, rooms.ts opens rooms with their profile,
|
|
4
4
|
// messages.ts turns stored records into messages and writes them (docs/spec/hub-1.md).
|
|
5
5
|
import { CAPABILITIES } from '../capabilities.js';
|
|
6
6
|
// The root principal of every hub and room (hub-1): the operator of the machine that made it.
|
|
@@ -8,7 +8,7 @@ export const OPERATOR = 'operator';
|
|
|
8
8
|
export const HUB_MAJOR = 1;
|
|
9
9
|
export const PROTOCOL_MAJOR = 1;
|
|
10
10
|
export const CAPS = new Set(Object.keys(CAPABILITIES));
|
|
11
|
-
export const RESERVED_ROOMS = new Set(['main'
|
|
11
|
+
export const RESERVED_ROOMS = new Set(['main']);
|
|
12
12
|
// Whether the room's work has started: it has the quorum of agents its genesis asked for (`Quorum`), or asked for none.
|
|
13
13
|
export const start = (R) => {
|
|
14
14
|
const agents = Object.values(R.members).filter(m => m.role !== 'root').length;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// messages: stored records read as messages, and a message written with the signals it made.
|
|
2
|
-
import
|
|
3
|
-
import { compose, decodeText, link, parse } from '../protocol/index.js';
|
|
2
|
+
import { decodeText, link, parse } from '../protocol/index.js';
|
|
4
3
|
import { emit } from '../signals/write.js';
|
|
5
4
|
export const toMessage = (R, from, x, seq) => {
|
|
6
5
|
const trust = R.hub.signed ? 'verified' : 'asserted';
|
|
@@ -11,10 +10,9 @@ export const toMessage = (R, from, x, seq) => {
|
|
|
11
10
|
return { ...base, invalid: d.invalid, subject: '', body: '', to: [], re: null, kind: null, state: null, assign: null, roles: null, hop: 0, bad: d.invalid, trailers: {} };
|
|
12
11
|
}
|
|
13
12
|
const p = parse(d.text, s => R.hub.store.isRef(s));
|
|
14
|
-
//
|
|
15
|
-
const ext = D.check(p, s => R.hub.store.isRef(s));
|
|
13
|
+
// The room profile's states are part of a message's validity.
|
|
16
14
|
const state = p.state && !p.bad && !R.profile.states.includes(p.state) ? `State ${p.state} is not in profile ${R.profile.name}` : null;
|
|
17
|
-
return { ...base, invalid: null, ...p, bad: p.bad ??
|
|
15
|
+
return { ...base, invalid: null, ...p, bad: p.bad ?? state };
|
|
18
16
|
};
|
|
19
17
|
// The room as stored, read without a reader's lock or state: for an author working out the signals of its own message
|
|
20
18
|
// (append runs inside reader locks too). Readers check signals themselves.
|
|
@@ -22,10 +20,8 @@ const asStored = (R) => {
|
|
|
22
20
|
const s = R.hub.store.room(R.name) ?? R.s;
|
|
23
21
|
return link(new Map(R.hub.store.logIds(s).map(id => [id, R.hub.store.messages(s, id, R.hub.store.logHead(s, id)).map((x, seq) => toMessage(R, id, x, seq))])));
|
|
24
22
|
};
|
|
25
|
-
// Whether a message can make signals: it opens, assigns or moves work
|
|
26
|
-
const
|
|
27
|
-
const movesWork = (p, R) => p.kind === R.profile.opens || !!p.assign || !!p.state;
|
|
28
|
-
const CARRIES = (p, R) => !p.bad && (movesWork(p, R) || DECISION_KINDS.includes(p.kind ?? ''));
|
|
23
|
+
// Whether a message can make signals: it opens, assigns or moves work.
|
|
24
|
+
const CARRIES = (p, R) => !p.bad && (p.kind === R.profile.opens || !!p.assign || !!p.state);
|
|
29
25
|
// A message on the author's log, and the signals it made (core/hub/signals/write.ts).
|
|
30
26
|
export const append = (R, who, text, entries = []) => {
|
|
31
27
|
const id = R.hub.store.append(R.s, who, text, entries);
|
|
@@ -43,5 +39,4 @@ export const withSizes = (R, m) => {
|
|
|
43
39
|
const sizes = R.hub.store.sizes(m.id, m.files.map(f => f.path));
|
|
44
40
|
return m.files.map(f => ({ ...f, size: sizes.get(f.path) ?? null }));
|
|
45
41
|
};
|
|
46
|
-
export const pongText = (me, m, what) => compose(`pong: ${me} ${what}`, [['To', m.from], ['Re', m.id], ['Kind', 'pong'], ['Hop', String(m.hopEff + 1)]]);
|
|
47
42
|
//# sourceMappingURL=messages.js.map
|