@volter/supercode-orchestrator 0.5.142 → 0.5.144
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/agents.mjs +17 -7
- package/board/dispatch.mjs +29 -9
- package/package.json +2 -2
package/agents.mjs
CHANGED
|
@@ -20,7 +20,8 @@ function store(root){
|
|
|
20
20
|
CREATE INDEX IF NOT EXISTS agent_active_assignments ON agent_assignments(org,agent_id,state);
|
|
21
21
|
CREATE TABLE IF NOT EXISTS agent_end_outbox(org TEXT NOT NULL,agent_id TEXT NOT NULL,reason TEXT NOT NULL,created_at INTEGER NOT NULL,PRIMARY KEY(org,agent_id));
|
|
22
22
|
CREATE TABLE IF NOT EXISTS agent_runtime_sessions(org TEXT NOT NULL,runtime_ref TEXT NOT NULL,session_ref TEXT NOT NULL,PRIMARY KEY(org,runtime_ref));
|
|
23
|
-
CREATE TABLE IF NOT EXISTS agent_runs(org TEXT NOT NULL,agent_id TEXT NOT NULL,session_ref TEXT NOT NULL,machine TEXT,run_id TEXT,profile_json TEXT,ended_at INTEGER,end_reason TEXT,main INTEGER NOT NULL DEFAULT 1,starter_session TEXT,usage_json TEXT,PRIMARY KEY(org,session_ref))
|
|
23
|
+
CREATE TABLE IF NOT EXISTS agent_runs(org TEXT NOT NULL,agent_id TEXT NOT NULL,session_ref TEXT NOT NULL,machine TEXT,run_id TEXT,profile_json TEXT,ended_at INTEGER,end_reason TEXT,main INTEGER NOT NULL DEFAULT 1,starter_session TEXT,usage_json TEXT,PRIMARY KEY(org,session_ref));
|
|
24
|
+
CREATE TABLE IF NOT EXISTS agent_usage_unread(org TEXT NOT NULL,session_ref TEXT NOT NULL,stamp TEXT NOT NULL,PRIMARY KEY(org,session_ref));`);
|
|
24
25
|
return db;
|
|
25
26
|
}
|
|
26
27
|
const digest=value=>createHash('sha256').update(value).digest('hex');
|
|
@@ -217,8 +218,8 @@ function adoptLocalOrg(root,scope){
|
|
|
217
218
|
db.prepare('INSERT OR IGNORE INTO agent_declaration_heads(org,key,agent_id,generation) VALUES (?,?,?,?)').run(scope.org,rekey(head.key),ids.get(head.agent_id)??head.agent_id,head.generation);
|
|
218
219
|
for(const [old,id] of ids)for(const table of ['agent_assignments','agent_runs','agent_end_outbox'])
|
|
219
220
|
db.prepare(`UPDATE OR IGNORE ${table} SET org=?,agent_id=? WHERE org='local' AND agent_id=?`).run(scope.org,id,old);
|
|
220
|
-
db.prepare(
|
|
221
|
-
for(const table of ['agent_intents','agent_declaration_heads','agent_assignments','agent_runs','agent_end_outbox','agent_runtime_sessions'])db.prepare(`DELETE FROM ${table} WHERE org='local'`).run();
|
|
221
|
+
for(const table of ['agent_runtime_sessions','agent_usage_unread'])db.prepare(`UPDATE OR IGNORE ${table} SET org=? WHERE org='local'`).run(scope.org);
|
|
222
|
+
for(const table of ['agent_intents','agent_declaration_heads','agent_assignments','agent_runs','agent_end_outbox','agent_runtime_sessions','agent_usage_unread'])db.prepare(`DELETE FROM ${table} WHERE org='local'`).run();
|
|
222
223
|
db.exec('COMMIT');
|
|
223
224
|
}catch(error){try{db.exec('ROLLBACK');}catch{}throw error;}finally{db.close();}
|
|
224
225
|
}
|
|
@@ -320,7 +321,7 @@ export function recordNativeAgentSession(root,runtimeRef,session) {
|
|
|
320
321
|
db.exec('BEGIN IMMEDIATE');
|
|
321
322
|
db.prepare('INSERT INTO agent_runtime_sessions(org,runtime_ref,session_ref) VALUES (?,?,?) ON CONFLICT(org,runtime_ref) DO UPDATE SET session_ref=excluded.session_ref').run(owner.org,runtimeRef,session);
|
|
322
323
|
// The lease-only row is not a session: its native session is the run now.
|
|
323
|
-
db.prepare(
|
|
324
|
+
for(const table of ['agent_runs','agent_usage_unread'])db.prepare(`DELETE FROM ${table} WHERE org=? AND session_ref=?`).run(owner.org,runtimeRef);
|
|
324
325
|
db.exec('COMMIT');
|
|
325
326
|
}catch(error){try{db.exec('ROLLBACK');}catch{}throw error;}finally{db.close();}
|
|
326
327
|
return agentRunForSession(root,[runtimeRef]);
|
|
@@ -349,11 +350,20 @@ export function agentRunForSession(root, references) {
|
|
|
349
350
|
}
|
|
350
351
|
/** G3: a session's usage as its harness recorded it, kept with the run that owned it. */
|
|
351
352
|
export function recordRunUsage(root,org,session,usage){
|
|
352
|
-
const db=store(root);try{
|
|
353
|
+
const db=store(root);try{
|
|
354
|
+
db.prepare('UPDATE agent_runs SET usage_json=? WHERE org=? AND session_ref=?').run(JSON.stringify(usage??[]),org,session);
|
|
355
|
+
db.prepare('DELETE FROM agent_usage_unread WHERE org=? AND session_ref=?').run(org,session);
|
|
356
|
+
}finally{db.close();}
|
|
357
|
+
}
|
|
358
|
+
/** A usage read its reader refused for what is in the session's record, kept with which reader that was and what the
|
|
359
|
+
* record was then (`stamp`): asked again once either differs. */
|
|
360
|
+
export function recordUsageUnread(root,org,session,stamp){
|
|
361
|
+
const db=store(root);try{db.prepare('INSERT INTO agent_usage_unread(org,session_ref,stamp) VALUES (?,?,?) ON CONFLICT(org,session_ref) DO UPDATE SET stamp=excluded.stamp').run(org,session,stamp);}finally{db.close();}
|
|
353
362
|
}
|
|
354
|
-
/** The runs whose ended sessions have no usage recorded yet
|
|
363
|
+
/** The runs whose ended sessions have no usage recorded yet, those never refused first; `usage_unread` is the stamp of
|
|
364
|
+
* a refused read. */
|
|
355
365
|
export function runsAwaitingUsage(root,org){
|
|
356
|
-
const db=store(root);try{return db.prepare('SELECT
|
|
366
|
+
const db=store(root);try{return db.prepare('SELECT r.*,u.stamp AS usage_unread FROM agent_runs r LEFT JOIN agent_usage_unread u ON u.org=r.org AND u.session_ref=r.session_ref WHERE r.org=? AND r.usage_json IS NULL ORDER BY u.stamp IS NOT NULL,r.rowid LIMIT 50').all(org);}finally{db.close();}
|
|
357
367
|
}
|
|
358
368
|
/** Usage records per agent id: every session this home ran for it. */
|
|
359
369
|
export function agentUsage(root){
|
package/board/dispatch.mjs
CHANGED
|
@@ -30,7 +30,7 @@ import { boardLabel, boardName } from '@volter/teams/board-name';
|
|
|
30
30
|
let fleetRoot = null;
|
|
31
31
|
import { inspectCompletionEvidence, noCodeChange, openReviewPullRequests, reviewDue, withCompletionEvidence, reviewCandidate, workflowRevision, retainWorkflowTick } from './ci.mjs';
|
|
32
32
|
import { runtimeDoor } from './runtime-door.mjs';
|
|
33
|
-
import { agentScope, localCardAgent, localReaderContext, localAgentSessions, runBudgetRefusal, runsAwaitingUsage, recordRunUsage, agentPreamble, agentEnvironment, recordAgentRun, activeAgentAssignments,finishAgentAssignment,handOffAgents, agentRunForSession } from '../agents.mjs';
|
|
33
|
+
import { agentScope, localCardAgent, localReaderContext, localAgentSessions, runBudgetRefusal, runsAwaitingUsage, recordRunUsage, recordUsageUnread, agentPreamble, agentEnvironment, recordAgentRun, activeAgentAssignments,finishAgentAssignment,handOffAgents, agentRunForSession } from '../agents.mjs';
|
|
34
34
|
import { materializeAgentHome } from '../agent-home.mjs';
|
|
35
35
|
import { memorySnapshot } from '../hermes-cli.mjs';
|
|
36
36
|
import { createRequire } from 'node:module';
|
|
@@ -1767,6 +1767,33 @@ export async function reconcileCallerLaunch(board, sid) {
|
|
|
1767
1767
|
}
|
|
1768
1768
|
}
|
|
1769
1769
|
|
|
1770
|
+
// The session-read doors' code for a file their reader cannot read for what is in it (session-file.mjs).
|
|
1771
|
+
const SESSION_UNREADABLE=-32050;
|
|
1772
|
+
/** An ended run's session usage, read from its harness's own records on its machine and kept with the run. One answer
|
|
1773
|
+
* is kept instead of asked for again: the reader's own refusal of the record for what is in it (SESSION_UNREADABLE).
|
|
1774
|
+
* It is kept with who refused (the machine's daemon: its id, release, and this run of it) and what the record was (its
|
|
1775
|
+
* file's size and time here, what discovery says of it on another machine), and asked again once either differs: the
|
|
1776
|
+
* same bytes get the same answer only from the same reader. Every other failure (no answer, a timeout, a fault in the
|
|
1777
|
+
* door, a daemon that does not say which it is) says nothing of the record, and the read is made again. */
|
|
1778
|
+
async function readRunUsage(root,org,row,endedAt){
|
|
1779
|
+
const [harness,...rest]=row.session_ref.split(':'),machine=row.machine&&normalMachine(row.machine)!==localMachine()?row.machine:null;
|
|
1780
|
+
// the usage door loads the session by its whole locator, which discovery names
|
|
1781
|
+
const sessionId=rest.join(':'),found=await runtimeDoor(machine,'harness.v1.sessions.discover',{query:sessionId,harnesses:[harness],limit:200},{timeout:30_000});
|
|
1782
|
+
const descriptor=found.code===0?json(found.stdout.trim(),{})?.sessions?.find(item=>item.locator?.harness===harness&&item.locator?.session_id===sessionId):null,locator=descriptor?.locator??null;
|
|
1783
|
+
let stamp=null;
|
|
1784
|
+
if(locator){
|
|
1785
|
+
const described=await runtimeDoor(machine,'harness.v1.machine.describe',{},{timeout:15_000}),reader=described.code===0?json(described.stdout.trim(),{}):null;
|
|
1786
|
+
const file=(()=>{if(machine||locator.storage?.kind!=='file')return null;try{const stat=statSync(locator.storage.path);return [stat.size,stat.mtimeMs];}catch{return null;}})();
|
|
1787
|
+
if(reader?.id&&reader.node?.release&&reader.node.started_at)stamp=JSON.stringify([reader.id,reader.node.release,reader.node.started_at,locator.storage,file??[descriptor.updated_at_ms??null,descriptor.message_count??null]]);
|
|
1788
|
+
}
|
|
1789
|
+
const refused=stamp!==null&&row.usage_unread===stamp;
|
|
1790
|
+
const reply=locator&&!refused?await runtimeDoor(machine,'harness.v1.sessions.usage',{locator},{timeout:30_000}):{code:1,stdout:'',stderr:''};
|
|
1791
|
+
if(reply.code===0)return recordRunUsage(root,org,row.session_ref,json(reply.stdout.trim(),{})?.usage??[]);
|
|
1792
|
+
// a session unreadable a day after its run ended is recorded as having no usage record, so it stops retrying
|
|
1793
|
+
if(now()-Number(endedAt)>86400)return recordRunUsage(root,org,row.session_ref,[]);
|
|
1794
|
+
if(stamp!==null&&!refused&&json(reply.stderr.trim(),{})?.code===SESSION_UNREADABLE)recordUsageUnread(root,org,row.session_ref,stamp);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1770
1797
|
/** The board's agent records: each run kept in the agent store, closed cards' assignments ended, declarations and ends
|
|
1771
1798
|
* handed to this machine's daemon, and usage read from each machine's own doors. No Teams call: the daemon declares and
|
|
1772
1799
|
* ends agents and binds their sessions (D137). It runs in the delivery pass (`deliver`), never in a dispatch round. */
|
|
@@ -1801,14 +1828,7 @@ async function synchronizeAgents(board, report) {
|
|
|
1801
1828
|
for(const row of runsAwaitingUsage(board.root,org)){
|
|
1802
1829
|
const run=agentRuns.find(item=>String(item.id)===String(row.run_id));
|
|
1803
1830
|
if(!run?.ended_at||read>=5)continue;
|
|
1804
|
-
|
|
1805
|
-
// the usage door loads the session by its whole locator, which discovery names
|
|
1806
|
-
const sessionId=rest.join(':'),found=await runtimeDoor(machine,'harness.v1.sessions.discover',{query:sessionId,harnesses:[harness],limit:200},{timeout:30_000});
|
|
1807
|
-
const locator=found.code===0?json(found.stdout.trim(),{})?.sessions?.find(item=>item.locator?.harness===harness&&item.locator?.session_id===sessionId)?.locator:null;
|
|
1808
|
-
const reply=locator?await runtimeDoor(machine,'harness.v1.sessions.usage',{locator},{timeout:30_000}):{code:1};read++;
|
|
1809
|
-
if(reply.code===0)recordRunUsage(board.root,org,row.session_ref,json(reply.stdout.trim(),{})?.usage??[]);
|
|
1810
|
-
// a session unreadable a day after its run ended is recorded as having no usage record, so it stops retrying
|
|
1811
|
-
else if(now()-Number(run.ended_at)>86400)recordRunUsage(board.root,org,row.session_ref,[]);
|
|
1831
|
+
await readRunUsage(board.root,org,row,run.ended_at);read++;
|
|
1812
1832
|
}
|
|
1813
1833
|
}
|
|
1814
1834
|
for(const assignment of activeAgentAssignments(board.root)){
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.144",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@volter/supercode-harness-sdk": "^0.3.91",
|
|
28
|
-
"@volter/teams": "0.2.
|
|
28
|
+
"@volter/teams": "0.2.59",
|
|
29
29
|
"@volter/ztrack": "^2.4.23",
|
|
30
30
|
"yaml": "^2.9.0"
|
|
31
31
|
},
|