instar 1.3.91 → 1.3.92
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/.claude/skills/autonomous/hooks/autonomous-stop-hook.sh +17 -0
- package/dashboard/index.html +108 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +207 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +30 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/config/LiveConfig.d.ts +3 -1
- package/dist/config/LiveConfig.d.ts.map +1 -1
- package/dist/config/LiveConfig.js +4 -1
- package/dist/config/LiveConfig.js.map +1 -1
- package/dist/config/stageWriteGuard.d.ts +23 -0
- package/dist/config/stageWriteGuard.d.ts.map +1 -0
- package/dist/config/stageWriteGuard.js +33 -0
- package/dist/config/stageWriteGuard.js.map +1 -0
- package/dist/core/DeliverMessageHandler.d.ts +33 -0
- package/dist/core/DeliverMessageHandler.d.ts.map +1 -0
- package/dist/core/DeliverMessageHandler.js +34 -0
- package/dist/core/DeliverMessageHandler.js.map +1 -0
- package/dist/core/LeaseCoordinator.d.ts +39 -2
- package/dist/core/LeaseCoordinator.d.ts.map +1 -1
- package/dist/core/LeaseCoordinator.js +45 -9
- package/dist/core/LeaseCoordinator.js.map +1 -1
- package/dist/core/MachineIdentity.d.ts +28 -1
- package/dist/core/MachineIdentity.d.ts.map +1 -1
- package/dist/core/MachineIdentity.js +81 -1
- package/dist/core/MachineIdentity.js.map +1 -1
- package/dist/core/MachineLoadBroker.d.ts +94 -0
- package/dist/core/MachineLoadBroker.d.ts.map +1 -0
- package/dist/core/MachineLoadBroker.js +87 -0
- package/dist/core/MachineLoadBroker.js.map +1 -0
- package/dist/core/MachinePoolRegistry.d.ts +94 -0
- package/dist/core/MachinePoolRegistry.d.ts.map +1 -0
- package/dist/core/MachinePoolRegistry.js +150 -0
- package/dist/core/MachinePoolRegistry.js.map +1 -0
- package/dist/core/MeshRpc.d.ts +165 -0
- package/dist/core/MeshRpc.d.ts.map +1 -0
- package/dist/core/MeshRpc.js +156 -0
- package/dist/core/MeshRpc.js.map +1 -0
- package/dist/core/MeshRpcClient.d.ts +59 -0
- package/dist/core/MeshRpcClient.d.ts.map +1 -0
- package/dist/core/MeshRpcClient.js +58 -0
- package/dist/core/MeshRpcClient.js.map +1 -0
- package/dist/core/MultiMachineCoordinator.d.ts +11 -0
- package/dist/core/MultiMachineCoordinator.d.ts.map +1 -1
- package/dist/core/MultiMachineCoordinator.js +13 -0
- package/dist/core/MultiMachineCoordinator.js.map +1 -1
- package/dist/core/NicknameAssigner.d.ts +51 -0
- package/dist/core/NicknameAssigner.d.ts.map +1 -0
- package/dist/core/NicknameAssigner.js +91 -0
- package/dist/core/NicknameAssigner.js.map +1 -0
- package/dist/core/NicknameCommand.d.ts +28 -0
- package/dist/core/NicknameCommand.d.ts.map +1 -0
- package/dist/core/NicknameCommand.js +69 -0
- package/dist/core/NicknameCommand.js.map +1 -0
- package/dist/core/PlacementExecutor.d.ts +71 -0
- package/dist/core/PlacementExecutor.d.ts.map +1 -0
- package/dist/core/PlacementExecutor.js +156 -0
- package/dist/core/PlacementExecutor.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +21 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/RebalancePlanner.d.ts +48 -0
- package/dist/core/RebalancePlanner.d.ts.map +1 -0
- package/dist/core/RebalancePlanner.js +71 -0
- package/dist/core/RebalancePlanner.js.map +1 -0
- package/dist/core/SessionOwnership.d.ts +103 -0
- package/dist/core/SessionOwnership.d.ts.map +1 -0
- package/dist/core/SessionOwnership.js +0 -0
- package/dist/core/SessionOwnership.js.map +1 -0
- package/dist/core/SessionOwnershipRegistry.d.ts +115 -0
- package/dist/core/SessionOwnershipRegistry.d.ts.map +1 -0
- package/dist/core/SessionOwnershipRegistry.js +128 -0
- package/dist/core/SessionOwnershipRegistry.js.map +1 -0
- package/dist/core/SessionPoolE2EResultStore.d.ts +45 -0
- package/dist/core/SessionPoolE2EResultStore.d.ts.map +1 -0
- package/dist/core/SessionPoolE2EResultStore.js +66 -0
- package/dist/core/SessionPoolE2EResultStore.js.map +1 -0
- package/dist/core/SessionRouter.d.ts +95 -0
- package/dist/core/SessionRouter.d.ts.map +1 -0
- package/dist/core/SessionRouter.js +151 -0
- package/dist/core/SessionRouter.js.map +1 -0
- package/dist/core/StageAdvancer.d.ts +47 -0
- package/dist/core/StageAdvancer.d.ts.map +1 -0
- package/dist/core/StageAdvancer.js +70 -0
- package/dist/core/StageAdvancer.js.map +1 -0
- package/dist/core/TransferByNickname.d.ts +51 -0
- package/dist/core/TransferByNickname.d.ts.map +1 -0
- package/dist/core/TransferByNickname.js +50 -0
- package/dist/core/TransferByNickname.js.map +1 -0
- package/dist/core/TransferOrchestrator.d.ts +113 -0
- package/dist/core/TransferOrchestrator.d.ts.map +1 -0
- package/dist/core/TransferOrchestrator.js +79 -0
- package/dist/core/TransferOrchestrator.js.map +1 -0
- package/dist/core/types.d.ts +177 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +5 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +8 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +4 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +14 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +12 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +91 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +5 -0
- package/upgrades/1.3.92.md +63 -0
- package/upgrades/side-effects/session-pool-capability-classification.md +16 -0
- package/upgrades/side-effects/session-pool-review-fixes.md +23 -0
- package/upgrades/side-effects/session-pool-track-a-router-lease.md +29 -0
- package/upgrades/side-effects/session-pool-track-b-agent-awareness.md +29 -0
- package/upgrades/side-effects/session-pool-track-b-nicknames.md +35 -0
- package/upgrades/side-effects/session-pool-track-b-pool-api.md +36 -0
- package/upgrades/side-effects/session-pool-track-b-registry.md +31 -0
- package/upgrades/side-effects/session-pool-track-b-wiring.md +39 -0
- package/upgrades/side-effects/session-pool-track-c-dispatcher.md +24 -0
- package/upgrades/side-effects/session-pool-track-c-meshrpc.md +35 -0
- package/upgrades/side-effects/session-pool-track-c-route-wiring.md +31 -0
- package/upgrades/side-effects/session-pool-track-d-ownership-fsm.md +31 -0
- package/upgrades/side-effects/session-pool-track-d-registry.md +27 -0
- package/upgrades/side-effects/session-pool-track-d-wiring.md +30 -0
- package/upgrades/side-effects/session-pool-track-e-boot-wiring.md +20 -0
- package/upgrades/side-effects/session-pool-track-e-placement-core.md +17 -0
- package/upgrades/side-effects/session-pool-track-e-router.md +23 -0
- package/upgrades/side-effects/session-pool-track-f-config.md +12 -0
- package/upgrades/side-effects/session-pool-track-f-handoff-core.md +17 -0
- package/upgrades/side-effects/session-pool-track-g-load-broker.md +16 -0
- package/upgrades/side-effects/session-pool-track-h-gate-wiring.md +17 -0
- package/upgrades/side-effects/session-pool-track-h-mesh-client.md +15 -0
- package/upgrades/side-effects/session-pool-track-h-rebalance.md +13 -0
- package/upgrades/side-effects/session-pool-track-h-rollout-gate.md +17 -0
- package/upgrades/side-effects/session-pool-track-h-stage-guard.md +16 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NicknameAssigner — derives a friendly, collision-free machine nickname.
|
|
3
|
+
*
|
|
4
|
+
* Multi-Machine Session Pool, spec §L2 "Machine Nicknames". Users name a
|
|
5
|
+
* machine by its nickname (the user-facing handle) rather than a raw machineId
|
|
6
|
+
* (`m_a3f9…`) — for the Machines dashboard tab and for "run this on <nickname>"
|
|
7
|
+
* / "move this to <nickname>" placement & transfer commands (§L4).
|
|
8
|
+
*
|
|
9
|
+
* This module is PURE LOGIC (no I/O): given a machine's own properties + the
|
|
10
|
+
* nicknames already in the pool, it produces a deterministic nickname. Keeping
|
|
11
|
+
* it pure makes the derivation + the collision rule unit-testable, and means
|
|
12
|
+
* every machine reading the same registry derives the same disambiguation.
|
|
13
|
+
*
|
|
14
|
+
* Assignment is auto + idempotent (a machine that already has a nickname keeps
|
|
15
|
+
* it — see MachineIdentityManager.registerMachine), and the result is always
|
|
16
|
+
* user-editable via PATCH /pool/machines/:id (§L2).
|
|
17
|
+
*/
|
|
18
|
+
/** Max nickname length (matches the validation regex below). */
|
|
19
|
+
const MAX_NICKNAME_LEN = 40;
|
|
20
|
+
/**
|
|
21
|
+
* Validation: a non-empty string starting with a word char, then up to 40 word
|
|
22
|
+
* chars / spaces / hyphens. Rejects control chars, leading separators, reserved
|
|
23
|
+
* shapes, and over-length values. Used at the editable surface (PATCH) and as a
|
|
24
|
+
* sanity gate on auto-derived values.
|
|
25
|
+
*/
|
|
26
|
+
export function isValidNickname(value) {
|
|
27
|
+
return typeof value === 'string' && /^[\w][\w \-]{0,40}$/.test(value.trim()) && value.trim().length > 0;
|
|
28
|
+
}
|
|
29
|
+
/** Title-case a space-separated phrase ("macbook pro" → "Macbook Pro"). */
|
|
30
|
+
function titleCase(s) {
|
|
31
|
+
return s
|
|
32
|
+
.split(/\s+/)
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
35
|
+
.join(' ');
|
|
36
|
+
}
|
|
37
|
+
/** Friendly label for a platform string like "darwin-arm64". */
|
|
38
|
+
function platformLabel(platform) {
|
|
39
|
+
const p = (platform || '').toLowerCase();
|
|
40
|
+
const os = p.startsWith('darwin')
|
|
41
|
+
? 'Mac'
|
|
42
|
+
: p.startsWith('linux')
|
|
43
|
+
? 'Linux'
|
|
44
|
+
: p.startsWith('win')
|
|
45
|
+
? 'Windows'
|
|
46
|
+
: 'Machine';
|
|
47
|
+
const archMatch = p.match(/-(arm64|x64|x86_64|arm|amd64)$/);
|
|
48
|
+
const arch = archMatch ? archMatch[1] : '';
|
|
49
|
+
return arch ? `${os} ${arch}` : os;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Derive a base (pre-collision) nickname from a machine's own properties.
|
|
53
|
+
* Prefers a sanitized hostname/identity-name; falls back to a platform label.
|
|
54
|
+
*
|
|
55
|
+
* @param identityName the machine's identity `name` (a sanitized hostname, e.g.
|
|
56
|
+
* "justins-macbook-pro") — may be empty.
|
|
57
|
+
* @param platform the platform string (e.g. "darwin-arm64") — fallback.
|
|
58
|
+
*/
|
|
59
|
+
export function deriveBaseNickname(identityName, platform) {
|
|
60
|
+
const fromName = titleCase((identityName || '').replace(/\.local$/i, '').replace(/[-_.]+/g, ' ').trim()).slice(0, MAX_NICKNAME_LEN);
|
|
61
|
+
if (fromName)
|
|
62
|
+
return fromName;
|
|
63
|
+
return platformLabel(platform);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Assign a collision-free nickname for a new machine.
|
|
67
|
+
*
|
|
68
|
+
* Deterministic: same (identityName, platform, existingNicknames) → same result.
|
|
69
|
+
* If the derived base collides (case-insensitive) with an existing nickname, a
|
|
70
|
+
* numeric suffix is appended ("mac mini" → "mac mini 2" → "mac mini 3"). The
|
|
71
|
+
* comparison is case-insensitive + trimmed so "Mac Mini" and "mac mini " collide.
|
|
72
|
+
*
|
|
73
|
+
* @param opts.identityName the machine's identity `name`.
|
|
74
|
+
* @param opts.platform the machine's platform string.
|
|
75
|
+
* @param opts.existingNicknames nicknames already taken in the pool.
|
|
76
|
+
*/
|
|
77
|
+
export function assignNickname(opts) {
|
|
78
|
+
const base = deriveBaseNickname(opts.identityName, opts.platform);
|
|
79
|
+
const taken = new Set((opts.existingNicknames || []).map((n) => n.trim().toLowerCase()));
|
|
80
|
+
if (!taken.has(base.toLowerCase()))
|
|
81
|
+
return base;
|
|
82
|
+
// Disambiguate with a numeric suffix, deterministically.
|
|
83
|
+
for (let n = 2; n < 1000; n++) {
|
|
84
|
+
const candidate = `${base} ${n}`;
|
|
85
|
+
if (!taken.has(candidate.toLowerCase()))
|
|
86
|
+
return candidate;
|
|
87
|
+
}
|
|
88
|
+
// Pathological (1000 same-named machines) — fall through to a base label.
|
|
89
|
+
return base;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=NicknameAssigner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NicknameAssigner.js","sourceRoot":"","sources":["../../src/core/NicknameAssigner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NicknameCommand — pure recognizer for the user's natural-language "move/run this
|
|
3
|
+
* on <machine-nickname>" requests (Multi-Machine Session Pool §L4). The headline
|
|
4
|
+
* test-as-self scenario: the user, mid-conversation, says "move this to the mini"
|
|
5
|
+
* and the session transfers to that machine. Per "Structure > Willpower" this is a
|
|
6
|
+
* deterministic recognizer over the KNOWN nickname set (resolved from the registry),
|
|
7
|
+
* NOT an open-ended LLM intent guess — so it can never invent a target machine.
|
|
8
|
+
*
|
|
9
|
+
* The recognizer is intentionally conservative: it only matches when an explicit
|
|
10
|
+
* relocation verb is present AND the tail resolves to a real, known nickname. A
|
|
11
|
+
* bare mention of a machine name ("the mini is fast") never triggers a transfer.
|
|
12
|
+
*/
|
|
13
|
+
export interface NicknameCommand {
|
|
14
|
+
/** 'transfer' (move an existing session) vs 'pin' (hard-pin future placement). */
|
|
15
|
+
intent: 'transfer' | 'pin';
|
|
16
|
+
/** The known nickname that was matched (canonical form as registered). */
|
|
17
|
+
nickname: string;
|
|
18
|
+
/** The verb phrase that triggered recognition (for audit/telemetry). */
|
|
19
|
+
matchedVerb: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Recognize a nickname relocation command in free text. Returns null if the text
|
|
23
|
+
* is not an explicit move/pin request OR if the target doesn't resolve to a known
|
|
24
|
+
* nickname. Matching is case-insensitive; when multiple nicknames could match, the
|
|
25
|
+
* LONGEST is chosen (so "mac mini" wins over "mini").
|
|
26
|
+
*/
|
|
27
|
+
export declare function recognizeNicknameCommand(text: string, knownNicknames: string[]): NicknameCommand | null;
|
|
28
|
+
//# sourceMappingURL=NicknameCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NicknameCommand.d.ts","sourceRoot":"","sources":["../../src/core/NicknameCommand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,MAAM,EAAE,UAAU,GAAG,KAAK,CAAC;IAC3B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;CACrB;AAeD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,eAAe,GAAG,IAAI,CAuCvG"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NicknameCommand — pure recognizer for the user's natural-language "move/run this
|
|
3
|
+
* on <machine-nickname>" requests (Multi-Machine Session Pool §L4). The headline
|
|
4
|
+
* test-as-self scenario: the user, mid-conversation, says "move this to the mini"
|
|
5
|
+
* and the session transfers to that machine. Per "Structure > Willpower" this is a
|
|
6
|
+
* deterministic recognizer over the KNOWN nickname set (resolved from the registry),
|
|
7
|
+
* NOT an open-ended LLM intent guess — so it can never invent a target machine.
|
|
8
|
+
*
|
|
9
|
+
* The recognizer is intentionally conservative: it only matches when an explicit
|
|
10
|
+
* relocation verb is present AND the tail resolves to a real, known nickname. A
|
|
11
|
+
* bare mention of a machine name ("the mini is fast") never triggers a transfer.
|
|
12
|
+
*/
|
|
13
|
+
// Relocation verbs → intent. "pin" is the only hard-pin verb; the rest are transfers.
|
|
14
|
+
const TRANSFER_VERBS = ['move', 'transfer', 'switch', 'migrate', 'send', 'shift', 'hand off', 'handoff', 'run', 'continue', 'resume', 'keep'];
|
|
15
|
+
const PIN_VERBS = ['pin', 'lock'];
|
|
16
|
+
// A relocation command must contain BOTH a verb and a "to/on/onto/over to" preposition
|
|
17
|
+
// before the target, OR an explicit "this/the conversation … on <nick>" shape. We
|
|
18
|
+
// anchor on the preposition so "the mini handled that" can't match.
|
|
19
|
+
const PREPOSITION = /\b(?:over to|onto|on to|to|on|at)\b/i;
|
|
20
|
+
function escapeRegExp(s) {
|
|
21
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Recognize a nickname relocation command in free text. Returns null if the text
|
|
25
|
+
* is not an explicit move/pin request OR if the target doesn't resolve to a known
|
|
26
|
+
* nickname. Matching is case-insensitive; when multiple nicknames could match, the
|
|
27
|
+
* LONGEST is chosen (so "mac mini" wins over "mini").
|
|
28
|
+
*/
|
|
29
|
+
export function recognizeNicknameCommand(text, knownNicknames) {
|
|
30
|
+
if (!text || typeof text !== 'string' || knownNicknames.length === 0)
|
|
31
|
+
return null;
|
|
32
|
+
// Find the EARLIEST-occurring relocation verb (pin verbs win on a positional tie).
|
|
33
|
+
const allVerbs = [
|
|
34
|
+
...PIN_VERBS.map((v) => ({ verb: v, intent: 'pin' })),
|
|
35
|
+
...TRANSFER_VERBS.map((v) => ({ verb: v, intent: 'transfer' })),
|
|
36
|
+
];
|
|
37
|
+
let chosenVerb = null;
|
|
38
|
+
let chosenIdx = Infinity;
|
|
39
|
+
let verbEnd = -1;
|
|
40
|
+
for (const cand of allVerbs) {
|
|
41
|
+
const m = new RegExp(`\\b${escapeRegExp(cand.verb)}\\b`, 'i').exec(text);
|
|
42
|
+
if (m && m.index < chosenIdx) {
|
|
43
|
+
chosenVerb = cand;
|
|
44
|
+
chosenIdx = m.index;
|
|
45
|
+
verbEnd = m.index + m[0].length;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!chosenVerb)
|
|
49
|
+
return null;
|
|
50
|
+
// There must be a preposition AFTER the verb (the target follows it).
|
|
51
|
+
const afterVerb = text.slice(verbEnd);
|
|
52
|
+
const prep = PREPOSITION.exec(afterVerb);
|
|
53
|
+
if (!prep)
|
|
54
|
+
return null;
|
|
55
|
+
const tail = afterVerb.slice(prep.index + prep[0].length);
|
|
56
|
+
const tailLower = tail.toLowerCase();
|
|
57
|
+
// Resolve the target against the KNOWN nickname set (longest match wins).
|
|
58
|
+
const matches = knownNicknames
|
|
59
|
+
.filter((n) => n && typeof n === 'string')
|
|
60
|
+
.filter((n) => {
|
|
61
|
+
const re = new RegExp(`(?:^|\\b|\\s)${escapeRegExp(n.toLowerCase())}(?:$|\\b|\\s|[.!?,])`, 'i');
|
|
62
|
+
return re.test(tailLower);
|
|
63
|
+
})
|
|
64
|
+
.sort((a, b) => b.length - a.length);
|
|
65
|
+
if (matches.length === 0)
|
|
66
|
+
return null;
|
|
67
|
+
return { intent: chosenVerb.intent, nickname: matches[0], matchedVerb: chosenVerb.verb };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=NicknameCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NicknameCommand.js","sourceRoot":"","sources":["../../src/core/NicknameCommand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAWH,sFAAsF;AACtF,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAC9I,MAAM,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAElC,uFAAuF;AACvF,kFAAkF;AAClF,oEAAoE;AACpE,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAE3D,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,IAAY,EAAE,cAAwB;IAC7E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAElF,mFAAmF;IACnF,MAAM,QAAQ,GAAwD;QACpE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,KAAc,EAAE,CAAC,CAAC;QAC9D,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,UAAmB,EAAE,CAAC,CAAC;KACzE,CAAC;IACF,IAAI,UAAU,GAAwD,IAAI,CAAC;IAC3E,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;YAC7B,UAAU,GAAG,IAAI,CAAC;YAClB,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC;YACpB,OAAO,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAClC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,sEAAsE;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAErC,0EAA0E;IAC1E,MAAM,OAAO,GAAG,cAAc;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC;SACzC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACZ,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,gBAAgB,YAAY,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;QAChG,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC;AAC3F,CAAC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlacementExecutor — the single canonical placement component (Multi-Machine
|
|
3
|
+
* Session Pool §L4). Per "Structure > Willpower", placement is NOT pluggable
|
|
4
|
+
* per-agent code: ALL placement decisions route through this one component, and
|
|
5
|
+
* its policy is structured DATA (JSON) validated against a fixed schema — never
|
|
6
|
+
* ad-hoc logic. `decide()` is PURE over its inputs (deterministic, unit-testable);
|
|
7
|
+
* the CALLER (router) performs the CAS + side effects from the returned decision.
|
|
8
|
+
*/
|
|
9
|
+
import type { MachineCapacity } from './types.js';
|
|
10
|
+
/** Per-topic placement metadata (persisted with the topic; §L4). */
|
|
11
|
+
export interface TopicPlacement {
|
|
12
|
+
/** A specific machine the session prefers / is pinned to (nickname-resolved → machineId). */
|
|
13
|
+
preferredMachine?: string;
|
|
14
|
+
/** Hard capability requirements (e.g. "gpu", "local-model:llama3"). */
|
|
15
|
+
requiredCapabilities?: string[];
|
|
16
|
+
/** When true + preferredMachine set → a HARD pin (queue+escalate if that machine is unavailable). */
|
|
17
|
+
pinned?: boolean;
|
|
18
|
+
lastTransferredAt?: number;
|
|
19
|
+
queueReason?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PlacementPolicy {
|
|
22
|
+
weights: {
|
|
23
|
+
loadAvg: number;
|
|
24
|
+
activeSessionRatio: number;
|
|
25
|
+
memPressure: number;
|
|
26
|
+
};
|
|
27
|
+
thresholds: {
|
|
28
|
+
rebalanceThresholdPercent: number;
|
|
29
|
+
placementHysteresisDelta: number;
|
|
30
|
+
};
|
|
31
|
+
capabilityWhitelist: string[];
|
|
32
|
+
ordering: Array<'hard-constraint' | 'pin' | 'sticky' | 'least-loaded'>;
|
|
33
|
+
}
|
|
34
|
+
export declare const DEFAULT_PLACEMENT_POLICY: PlacementPolicy;
|
|
35
|
+
export interface PlacementRequest {
|
|
36
|
+
sessionKey: string;
|
|
37
|
+
topicMetadata: TopicPlacement;
|
|
38
|
+
machineRegistry: MachineCapacity[];
|
|
39
|
+
currentOwner?: string;
|
|
40
|
+
reason: 'new' | 'failover' | 'rebalance' | 'pin';
|
|
41
|
+
}
|
|
42
|
+
export interface PlacementDecision {
|
|
43
|
+
chosenMachine: string | null;
|
|
44
|
+
score: number;
|
|
45
|
+
reason: string;
|
|
46
|
+
outcome: 'placed' | 'queued' | 'placement-blocked';
|
|
47
|
+
escalationReason?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate a PlacementPolicy against the fixed schema (§L4). Throws on a malformed
|
|
51
|
+
* policy — unknown weight key, non-numeric threshold/weight, or a capability token
|
|
52
|
+
* outside the whitelist vocabulary — so a router refuses to act with a bad policy
|
|
53
|
+
* (never silently defaulted). Called at startup.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validatePlacementPolicy(policy: unknown): asserts policy is PlacementPolicy;
|
|
56
|
+
/** Validate per-topic placement metadata on read (§L4) — never infer/sanitize; block+escalate on a violation. */
|
|
57
|
+
export declare function validateTopicPlacement(tp: unknown, capabilityWhitelist: string[]): {
|
|
58
|
+
ok: true;
|
|
59
|
+
} | {
|
|
60
|
+
ok: false;
|
|
61
|
+
reason: string;
|
|
62
|
+
};
|
|
63
|
+
export declare class PlacementExecutor {
|
|
64
|
+
private readonly policy;
|
|
65
|
+
constructor(policy?: PlacementPolicy);
|
|
66
|
+
/** Score a candidate (lower = better): weighted load + session-ratio + mem pressure. */
|
|
67
|
+
private score;
|
|
68
|
+
/** Pure placement decision (§L4). Deterministic over its inputs. */
|
|
69
|
+
decide(req: PlacementRequest): PlacementDecision;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=PlacementExecutor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlacementExecutor.d.ts","sourceRoot":"","sources":["../../src/core/PlacementExecutor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,oEAAoE;AACpE,MAAM,WAAW,cAAc;IAC7B,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,qGAAqG;IACrG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9E,UAAU,EAAE;QAAE,yBAAyB,EAAE,MAAM,CAAC;QAAC,wBAAwB,EAAE,MAAM,CAAA;KAAE,CAAC;IACpF,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,KAAK,CAAC,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,cAAc,CAAC,CAAC;CACxE;AAED,eAAO,MAAM,wBAAwB,EAAE,eAKtC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,cAAc,CAAC;IAC9B,eAAe,EAAE,eAAe,EAAE,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,KAAK,CAAC;CAClD;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,mBAAmB,CAAC;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,eAAe,CAqB1F;AAED,iHAAiH;AACjH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAqB/H;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;gBAC7B,MAAM,GAAE,eAA0C;IAK9D,wFAAwF;IACxF,OAAO,CAAC,KAAK;IAQb,oEAAoE;IACpE,MAAM,CAAC,GAAG,EAAE,gBAAgB,GAAG,iBAAiB;CA6DjD"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PlacementExecutor — the single canonical placement component (Multi-Machine
|
|
3
|
+
* Session Pool §L4). Per "Structure > Willpower", placement is NOT pluggable
|
|
4
|
+
* per-agent code: ALL placement decisions route through this one component, and
|
|
5
|
+
* its policy is structured DATA (JSON) validated against a fixed schema — never
|
|
6
|
+
* ad-hoc logic. `decide()` is PURE over its inputs (deterministic, unit-testable);
|
|
7
|
+
* the CALLER (router) performs the CAS + side effects from the returned decision.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_PLACEMENT_POLICY = {
|
|
10
|
+
weights: { loadAvg: 1.0, activeSessionRatio: 1.0, memPressure: 0.5 },
|
|
11
|
+
thresholds: { rebalanceThresholdPercent: 0.85, placementHysteresisDelta: 0.15 },
|
|
12
|
+
capabilityWhitelist: ['gpu', 'fast-cpu', 'sessions', 'jobs', 'telegram', 'tunnel'],
|
|
13
|
+
ordering: ['hard-constraint', 'pin', 'sticky', 'least-loaded'],
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Validate a PlacementPolicy against the fixed schema (§L4). Throws on a malformed
|
|
17
|
+
* policy — unknown weight key, non-numeric threshold/weight, or a capability token
|
|
18
|
+
* outside the whitelist vocabulary — so a router refuses to act with a bad policy
|
|
19
|
+
* (never silently defaulted). Called at startup.
|
|
20
|
+
*/
|
|
21
|
+
export function validatePlacementPolicy(policy) {
|
|
22
|
+
const p = policy;
|
|
23
|
+
if (!p || typeof p !== 'object')
|
|
24
|
+
throw new Error('PlacementPolicy must be an object');
|
|
25
|
+
const wKeys = ['loadAvg', 'activeSessionRatio', 'memPressure'];
|
|
26
|
+
if (!p.weights || typeof p.weights !== 'object')
|
|
27
|
+
throw new Error('PlacementPolicy.weights required');
|
|
28
|
+
for (const k of Object.keys(p.weights)) {
|
|
29
|
+
if (!wKeys.includes(k))
|
|
30
|
+
throw new Error(`PlacementPolicy.weights: unknown key '${k}'`);
|
|
31
|
+
}
|
|
32
|
+
for (const k of wKeys) {
|
|
33
|
+
if (typeof p.weights[k] !== 'number')
|
|
34
|
+
throw new Error(`PlacementPolicy.weights.${k} must be a number`);
|
|
35
|
+
}
|
|
36
|
+
if (!p.thresholds || typeof p.thresholds.rebalanceThresholdPercent !== 'number' || typeof p.thresholds.placementHysteresisDelta !== 'number') {
|
|
37
|
+
throw new Error('PlacementPolicy.thresholds.{rebalanceThresholdPercent,placementHysteresisDelta} must be numbers');
|
|
38
|
+
}
|
|
39
|
+
if (!Array.isArray(p.capabilityWhitelist) || p.capabilityWhitelist.some((c) => typeof c !== 'string')) {
|
|
40
|
+
throw new Error('PlacementPolicy.capabilityWhitelist must be string[]');
|
|
41
|
+
}
|
|
42
|
+
const validSteps = ['hard-constraint', 'pin', 'sticky', 'least-loaded'];
|
|
43
|
+
if (!Array.isArray(p.ordering) || p.ordering.some((s) => !validSteps.includes(s))) {
|
|
44
|
+
throw new Error(`PlacementPolicy.ordering must be a subset of ${validSteps.join(',')}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Validate per-topic placement metadata on read (§L4) — never infer/sanitize; block+escalate on a violation. */
|
|
48
|
+
export function validateTopicPlacement(tp, capabilityWhitelist) {
|
|
49
|
+
if (tp == null)
|
|
50
|
+
return { ok: true };
|
|
51
|
+
if (typeof tp !== 'object')
|
|
52
|
+
return { ok: false, reason: 'topic-metadata-not-object' };
|
|
53
|
+
const t = tp;
|
|
54
|
+
if ('preferredMachine' in t && t.preferredMachine != null && (typeof t.preferredMachine !== 'string' || !/^[\w-]+$/.test(t.preferredMachine))) {
|
|
55
|
+
return { ok: false, reason: 'preferredMachine-invalid' };
|
|
56
|
+
}
|
|
57
|
+
if ('pinned' in t && t.pinned != null && typeof t.pinned !== 'boolean')
|
|
58
|
+
return { ok: false, reason: 'pinned-not-boolean' };
|
|
59
|
+
// A hard pin (pinned:true) is meaningless without a target machine — reject it rather
|
|
60
|
+
// than silently treating it as unpinned (which would let placement drift off the
|
|
61
|
+
// machine the user explicitly pinned to). (2026-05-29 pre-merge review.)
|
|
62
|
+
if (t.pinned === true && (t.preferredMachine == null || t.preferredMachine === '')) {
|
|
63
|
+
return { ok: false, reason: 'pinned-without-target' };
|
|
64
|
+
}
|
|
65
|
+
if ('requiredCapabilities' in t && t.requiredCapabilities != null) {
|
|
66
|
+
if (!Array.isArray(t.requiredCapabilities))
|
|
67
|
+
return { ok: false, reason: 'requiredCapabilities-not-array' };
|
|
68
|
+
for (const c of t.requiredCapabilities) {
|
|
69
|
+
if (typeof c !== 'string' || !capabilityWhitelist.includes(c))
|
|
70
|
+
return { ok: false, reason: `capability-not-whitelisted:${String(c)}` };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { ok: true };
|
|
74
|
+
}
|
|
75
|
+
const MEM_PRESSURE_NUM = { low: 0, moderate: 1, high: 2, critical: 3 };
|
|
76
|
+
export class PlacementExecutor {
|
|
77
|
+
policy;
|
|
78
|
+
constructor(policy = DEFAULT_PLACEMENT_POLICY) {
|
|
79
|
+
validatePlacementPolicy(policy);
|
|
80
|
+
this.policy = policy;
|
|
81
|
+
}
|
|
82
|
+
/** Score a candidate (lower = better): weighted load + session-ratio + mem pressure. */
|
|
83
|
+
score(m) {
|
|
84
|
+
const w = this.policy.weights;
|
|
85
|
+
const load = m.loadAvg ?? 0;
|
|
86
|
+
const ratio = m.maxSessions && m.maxSessions > 0 ? (m.activeSessionCount ?? 0) / m.maxSessions : 0;
|
|
87
|
+
const mem = MEM_PRESSURE_NUM[m.memPressure ?? 'low'] ?? 0;
|
|
88
|
+
return w.loadAvg * load + w.activeSessionRatio * ratio + w.memPressure * mem;
|
|
89
|
+
}
|
|
90
|
+
/** Pure placement decision (§L4). Deterministic over its inputs. */
|
|
91
|
+
decide(req) {
|
|
92
|
+
// 0. Validate the topic metadata on read — never mis-place on corrupt metadata.
|
|
93
|
+
const v = validateTopicPlacement(req.topicMetadata, this.policy.capabilityWhitelist);
|
|
94
|
+
if (!v.ok) {
|
|
95
|
+
return { chosenMachine: null, score: 0, reason: 'topic-metadata-invalid', outcome: 'placement-blocked', escalationReason: v.reason };
|
|
96
|
+
}
|
|
97
|
+
const tp = req.topicMetadata ?? {};
|
|
98
|
+
// Eligible = online (clock-ok machines are already excluded upstream via the registry,
|
|
99
|
+
// but we also honor an explicit offline flag here for determinism).
|
|
100
|
+
let candidates = req.machineRegistry.filter((m) => m.online && m.clockSkewStatus !== 'suspect-clock-removed');
|
|
101
|
+
for (const step of this.policy.ordering) {
|
|
102
|
+
if (step === 'hard-constraint') {
|
|
103
|
+
// Required capabilities.
|
|
104
|
+
if (tp.requiredCapabilities && tp.requiredCapabilities.length > 0) {
|
|
105
|
+
candidates = candidates.filter((m) => tp.requiredCapabilities.every((c) => (m.capabilities ?? []).includes(c)));
|
|
106
|
+
if (candidates.length === 0) {
|
|
107
|
+
return { chosenMachine: null, score: 0, reason: 'no-capable-machine', outcome: 'queued', escalationReason: 'capabilities-unsatisfiable' };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Hard pin: the named machine MUST run it; queue+escalate if unavailable (never re-route).
|
|
111
|
+
if (tp.pinned && tp.preferredMachine) {
|
|
112
|
+
const pinned = candidates.find((m) => m.machineId === tp.preferredMachine);
|
|
113
|
+
if (!pinned) {
|
|
114
|
+
return { chosenMachine: null, score: 0, reason: 'hard-pin-unavailable', outcome: 'queued', escalationReason: 'hard-pin-unsatisfiable' };
|
|
115
|
+
}
|
|
116
|
+
return { chosenMachine: pinned.machineId, score: this.score(pinned), reason: 'hard-pin', outcome: 'placed' };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else if (step === 'pin') {
|
|
120
|
+
// Soft preference: prefer the named machine if eligible; else fall through (degrade).
|
|
121
|
+
if (tp.preferredMachine) {
|
|
122
|
+
const pref = candidates.find((m) => m.machineId === tp.preferredMachine);
|
|
123
|
+
if (pref)
|
|
124
|
+
return { chosenMachine: pref.machineId, score: this.score(pref), reason: 'preference', outcome: 'placed' };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (step === 'sticky') {
|
|
128
|
+
// Keep the current owner unless it's under pressure (hysteresis) or being rebalanced.
|
|
129
|
+
if (req.currentOwner && req.reason !== 'rebalance') {
|
|
130
|
+
const cur = candidates.find((m) => m.machineId === req.currentOwner);
|
|
131
|
+
if (cur) {
|
|
132
|
+
const best = candidates.reduce((a, b) => (this.score(b) < this.score(a) ? b : a), candidates[0]);
|
|
133
|
+
// Stick unless a meaningfully-better machine exists (hysteresis margin).
|
|
134
|
+
if (this.score(cur) <= this.score(best) + this.policy.thresholds.placementHysteresisDelta) {
|
|
135
|
+
return { chosenMachine: cur.machineId, score: this.score(cur), reason: 'sticky', outcome: 'placed' };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (step === 'least-loaded') {
|
|
141
|
+
if (candidates.length === 0) {
|
|
142
|
+
return { chosenMachine: null, score: 0, reason: 'no-online-machine', outcome: 'queued', escalationReason: 'no-capable-online-machine' };
|
|
143
|
+
}
|
|
144
|
+
const best = candidates.reduce((a, b) => (this.score(b) < this.score(a) ? b : a), candidates[0]);
|
|
145
|
+
return { chosenMachine: best.machineId, score: this.score(best), reason: 'least-loaded', outcome: 'placed' };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Ordering exhausted with no placement (e.g. no least-loaded step + no pin matched).
|
|
149
|
+
if (candidates.length === 0) {
|
|
150
|
+
return { chosenMachine: null, score: 0, reason: 'no-online-machine', outcome: 'queued', escalationReason: 'no-capable-online-machine' };
|
|
151
|
+
}
|
|
152
|
+
const best = candidates.reduce((a, b) => (this.score(b) < this.score(a) ? b : a), candidates[0]);
|
|
153
|
+
return { chosenMachine: best.machineId, score: this.score(best), reason: 'fallback-least-loaded', outcome: 'placed' };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=PlacementExecutor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlacementExecutor.js","sourceRoot":"","sources":["../../src/core/PlacementExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PostUpdateMigrator.d.ts","sourceRoot":"","sources":["../../src/core/PostUpdateMigrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsCH,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC3B,MAAM,yBAAyB,CAAC;AAIjC,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kCAAkC;IAClC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2CAA2C;IAC3C,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,CAAiC;gBAEvC,MAAM,EAAE,cAAc;IAIlC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,oBAAoB;IAoB5B;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAItC;;;;;;OAMG;IACG,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC;IAIjC,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,kBAAkB;IAO1B;;;;OAIG;IACH,OAAO,CAAC,YAAY;IASpB;;;OAGG;IACH,OAAO,IAAI,eAAe;IA4C1B;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,0BAA0B;IAsElC,OAAO,CAAC,0BAA0B;IAmDlC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kCAAkC;IAwH1C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,kCAAkC;IA8C1C,OAAO,CAAC,uBAAuB;IAwE/B,OAAO,CAAC,4CAA4C;IA+CpD,OAAO,CAAC,yBAAyB;IA6FjC;;;;;;;;;;OAUG;IACG,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC;YA4BhC,uBAAuB;IAkGrC,OAAO,CAAC,0BAA0B;IAkGlC,OAAO,CAAC,0BAA0B;IAkElC,OAAO,CAAC,oBAAoB;IA4G5B,OAAO,CAAC,8BAA8B;IA2EtC;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,0BAA0B;IA8BlC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,4BAA4B;IAwBpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,sBAAsB;IAwB9B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,mCAAmC;IA4C3C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAW5B;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,yBAAyB;IA4HjC;6EACyE;IACzE,OAAO,CAAC,wBAAwB;IAShC;sDACkD;IAClD,OAAO,CAAC,wBAAwB;IAQhC;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAoOpB;;;;;;;;OAQG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI;IA6CvE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAiEzB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAkChC;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB;IAoEhC;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAiE5B;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IA8BnC;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAiHhC;;;;;;OAMG;IACH,OAAO,CAAC,8BAA8B;IAwDtC,OAAO,CAAC,0BAA0B;IA8DlC;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkiCvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,kCAAkC;IAmG1C;;;OAGG;IACH,OAAO,CAAC,cAAc;IA6ItB;;;OAGG;IACH,OAAO,CAAC,eAAe;IA0SvB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAqFrB;;;OAGG;IACH;;;OAGG;IACH;;;;;;;;;;;;;;;;;;OAkBG;IACH,OAAO,CAAC,wBAAwB;IAmEhC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,6BAA6B;IAwDrC;;;;;;;;;OASG;IACH,OAAO,CAAC,yBAAyB;IAsDjC,OAAO,CAAC,wBAAwB;IAqChC,OAAO,CAAC,gBAAgB;IAiBxB;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,CAAC,qBAAqB;IAkE7B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,0BAA0B;IAgDlC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAkC5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IA2C1B;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,oBAAoB;IAgC5B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAqC9B;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,qBAAqB,GAAG,yBAAyB,GAAG,mBAAmB,GAAG,iBAAiB,GAAG,wBAAwB,GAAG,8BAA8B,GAAG,2BAA2B,GAAG,4BAA4B,GAAG,iBAAiB,GAAG,0BAA0B,GAAG,wBAAwB,GAAG,iBAAiB,GAAG,kBAAkB,GAAG,0BAA0B,GAAG,uBAAuB,GAAG,iBAAiB,GAAG,MAAM;IAsBpc,oFAAoF;IACpF,iCAAiC,IAAI,MAAM;IAI3C,6EAA6E;IAC7E,yBAAyB,IAAI,MAAM;IAInC,OAAO,CAAC,mBAAmB;IAoY3B,OAAO,CAAC,wBAAwB;IA8EhC,OAAO,CAAC,2BAA2B;IAoEnC,OAAO,CAAC,yBAAyB;IA8IjC,OAAO,CAAC,2BAA2B;IAqInC,OAAO,CAAC,qBAAqB;IAqP7B,OAAO,CAAC,uBAAuB;IAqJ/B,OAAO,CAAC,qBAAqB;IA8H7B,OAAO,CAAC,2BAA2B;IAoHnC,OAAO,CAAC,iCAAiC;IA6DzC,OAAO,CAAC,4BAA4B;IAsMpC;;;;;;;;;;OAUG;IACH;;;;;;;;;;;OAWG;IAEH,gBAAuB,iCAAiC,EAAE,WAAW,CAAC,MAAM,CAAC,CA4B1E;IAEH;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,8BAA8B;IAiHtC,OAAO,CAAC,uBAAuB;IA4B/B;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,sBAAsB;IAwC9B,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,8BAA8B;IA6HtC,OAAO,CAAC,+BAA+B;IAwKvC,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,qBAAqB;IAqO7B,OAAO,CAAC,qBAAqB;IAqI7B,OAAO,CAAC,qBAAqB;IAuL7B,OAAO,CAAC,6BAA6B;IAkLrC,OAAO,CAAC,0BAA0B;IAgClC,OAAO,CAAC,gBAAgB;IAmJxB,OAAO,CAAC,6BAA6B;CAoCtC"}
|
|
@@ -2627,6 +2627,27 @@ The attention queue spawns ONE Telegram forum topic per item — right for a gen
|
|
|
2627
2627
|
else {
|
|
2628
2628
|
result.skipped.push('CLAUDE.md: Topic-Flood Guard section already present');
|
|
2629
2629
|
}
|
|
2630
|
+
// Multi-Machine Session Pool (§L2) — tells the agent about the active-active
|
|
2631
|
+
// pool + the Machines tab + nickname-based placement/transfer. Without it an
|
|
2632
|
+
// agent asked "what machines am I on / move this to the mini / where is this
|
|
2633
|
+
// running?" has no grounded answer. Idempotent via the unique marker phrase.
|
|
2634
|
+
if (!content.includes('Multi-Machine Session Pool (active-active') && !content.includes('/pool/machines/')) {
|
|
2635
|
+
const section = `
|
|
2636
|
+
## Multi-Machine Session Pool (active-active — spread conversations across machines)
|
|
2637
|
+
|
|
2638
|
+
Beyond the one-awake-machine model: with the pool enabled I run conversations across ALL my machines at once and can MOVE a conversation between them. Ships DARK behind \`multiMachine.sessionPool.stage\` (default 'dark'); a single-machine agent is a no-op.
|
|
2639
|
+
|
|
2640
|
+
- **See the pool:** the **Machines tab** in the dashboard, or \`GET /pool\` (Bearer-auth) → which machine is the router ("dispatcher") + every machine's nickname, hardware, online status, load, and clock-skew status.
|
|
2641
|
+
- **Machine nicknames** are the user-facing handle (auto-assigned, editable). Rename via \`PATCH /pool/machines/:machineId\` with \`{"nickname":"the mini"}\`, or inline on the Machines tab.
|
|
2642
|
+
- **Proactive triggers:** when the user says "run this on <nickname>" / "move this to <nickname>" → placement/transfer-by-nickname (the session moves to the named machine, resuming like a session restart). "where is this running?" → \`GET /pool\`. Deep mechanics: the Machines tab + \`docs/specs/MULTI-MACHINE-SESSION-POOL-SPEC.md\`.
|
|
2643
|
+
`;
|
|
2644
|
+
content += '\n' + section;
|
|
2645
|
+
patched = true;
|
|
2646
|
+
result.upgraded.push('CLAUDE.md: added Multi-Machine Session Pool section');
|
|
2647
|
+
}
|
|
2648
|
+
else {
|
|
2649
|
+
result.skipped.push('CLAUDE.md: Multi-Machine Session Pool section already present');
|
|
2650
|
+
}
|
|
2630
2651
|
// ContextWedgeSentinel — the 4th silently-stopped sentinel. Tells the agent
|
|
2631
2652
|
// about the thinking-block-400 wedge + that auto-recovery is opt-in. Without
|
|
2632
2653
|
// it, an agent asked "why did my session keep failing instantly / what is
|