@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 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("UPDATE OR IGNORE agent_runtime_sessions SET org=? WHERE org='local'").run(scope.org);
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('DELETE FROM agent_runs WHERE org=? AND session_ref=?').run(owner.org,runtimeRef);
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{db.prepare('UPDATE agent_runs SET usage_json=? WHERE org=? AND session_ref=?').run(JSON.stringify(usage??[]),org,session);}finally{db.close();}
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 * FROM agent_runs WHERE org=? AND usage_json IS NULL ORDER BY rowid LIMIT 50').all(org);}finally{db.close();}
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){
@@ -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
- const [harness,...rest]=row.session_ref.split(':'),machine=row.machine&&normalMachine(row.machine)!==localMachine()?row.machine:null;
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.142",
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.58",
28
+ "@volter/teams": "0.2.59",
29
29
  "@volter/ztrack": "^2.4.23",
30
30
  "yaml": "^2.9.0"
31
31
  },