@volter/supercode-interchange 0.1.22 → 0.1.24

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.
Files changed (129) hide show
  1. package/LICENSE-diffy-MIT +23 -0
  2. package/README.md +422 -2
  3. package/channel-records.mjs +71 -0
  4. package/channel-session-counts.mjs +81 -0
  5. package/channels-read.mjs +90 -0
  6. package/corpus-audit-records.mjs +192 -0
  7. package/corpus-audit-render.mjs +47 -0
  8. package/corpus-audit-schema.mjs +89 -0
  9. package/corpus-audit.d.ts +16 -0
  10. package/corpus-audit.mjs +132 -0
  11. package/file-io.mjs +21 -2
  12. package/harness-settings.mjs +19 -0
  13. package/hermes-fire-links.mjs +135 -0
  14. package/hermes-fires.mjs +106 -0
  15. package/hermes-read-home.d.ts +3 -0
  16. package/hermes-read-home.mjs +235 -0
  17. package/hermes-read-store.mjs +49 -0
  18. package/hermes-read-validation.mjs +170 -0
  19. package/hermes-read-workflow.mjs +164 -0
  20. package/hermes-read-yaml.mjs +68 -0
  21. package/hermes-store.mjs +1 -1
  22. package/homes.mjs +2 -2
  23. package/index.d.ts +14 -1
  24. package/index.mjs +1 -1
  25. package/inspection-builtin-tools.json +261 -0
  26. package/inspection-config-fields.json +765 -0
  27. package/inspection-schemas.d.ts +4 -0
  28. package/inspection-schemas.mjs +117 -0
  29. package/job-notepad-read.mjs +97 -0
  30. package/jobs-control.mjs +250 -0
  31. package/jobs-read.mjs +198 -0
  32. package/jsonl.mjs +4 -3
  33. package/memory-show.mjs +187 -0
  34. package/metadata.mjs +45 -3
  35. package/native-json.mjs +46 -3
  36. package/ontology-contracts.d.ts +5 -0
  37. package/ontology-contracts.mjs +29 -0
  38. package/ontology-operations.json +198 -0
  39. package/ontology-schemas.json +7946 -0
  40. package/openclaw-jobs-read.mjs +117 -0
  41. package/openclaw-profile-home.mjs +142 -0
  42. package/orchestrator-read-records.mjs +112 -0
  43. package/orchestrator-store.mjs +21 -9
  44. package/package.json +59 -4
  45. package/parity-ledger.json +6984 -0
  46. package/parity-orchestration.json +5538 -0
  47. package/parity-report.d.ts +7 -0
  48. package/parity-report.mjs +46 -0
  49. package/preset-profiles.mjs +27 -0
  50. package/profile-command.mjs +32 -0
  51. package/profile-orchestrator-door.mjs +104 -0
  52. package/profile-paths.mjs +25 -0
  53. package/profiles-control.mjs +98 -0
  54. package/profiles-read.mjs +119 -0
  55. package/reduction-attribution.mjs +54 -0
  56. package/reduction-diff.mjs +165 -0
  57. package/reduction-log.mjs +177 -0
  58. package/reduction-view.mjs +145 -0
  59. package/retained-projection.mjs +9 -0
  60. package/routes-read.mjs +103 -0
  61. package/runs-read.mjs +224 -0
  62. package/session-activity.d.ts +23 -0
  63. package/session-activity.mjs +124 -0
  64. package/session-append-scan.mjs +27 -17
  65. package/session-branch-handoff.mjs +115 -0
  66. package/session-claude-graph.mjs +29 -7
  67. package/session-claude-index.mjs +732 -114
  68. package/session-claude-runtime.d.ts +28 -0
  69. package/session-claude-runtime.mjs +171 -6
  70. package/session-claude-scan-store.mjs +164 -0
  71. package/session-claude.mjs +9 -0
  72. package/session-codex-activity.d.ts +6 -0
  73. package/session-codex-activity.mjs +120 -0
  74. package/session-codex-window.mjs +24 -4
  75. package/session-config-scan.mjs +141 -0
  76. package/session-convert.d.ts +33 -0
  77. package/session-convert.mjs +179 -0
  78. package/session-emission-version.json +1 -0
  79. package/session-file.mjs +11 -1
  80. package/session-follower.mjs +17 -5
  81. package/session-fresh-id.mjs +15 -0
  82. package/session-hermes-import.mjs +61 -0
  83. package/session-hermes.mjs +1 -1
  84. package/session-import.mjs +37 -0
  85. package/session-inspect.d.ts +18 -0
  86. package/session-inspect.mjs +122 -0
  87. package/session-machine-tree.d.ts +33 -0
  88. package/session-machine-tree.mjs +274 -0
  89. package/session-materialize-files.mjs +78 -0
  90. package/session-materialize-opencode.mjs +137 -0
  91. package/session-materialize.mjs +186 -0
  92. package/session-message.mjs +85 -0
  93. package/session-model.d.ts +9 -0
  94. package/session-model.mjs +1 -0
  95. package/session-opencode-store.mjs +39 -3
  96. package/session-path.mjs +6 -3
  97. package/session-pi-content.mjs +7 -2
  98. package/session-read.d.ts +134 -2
  99. package/session-read.mjs +86 -9
  100. package/session-recorded-config.mjs +3 -2
  101. package/session-records.mjs +12 -6
  102. package/session-residue-store.mjs +90 -0
  103. package/session-residue.mjs +150 -0
  104. package/session-resume-instructions.mjs +1 -1
  105. package/session-saved-read.mjs +105 -0
  106. package/session-transfer.mjs +141 -0
  107. package/session-views.mjs +1 -0
  108. package/session-write-claude.mjs +135 -0
  109. package/session-write-codex.mjs +146 -0
  110. package/session-write-gemini.mjs +77 -0
  111. package/session-write-goose.mjs +99 -0
  112. package/session-write-grok.mjs +70 -0
  113. package/session-write-native.mjs +41 -0
  114. package/session-write-opencode.mjs +197 -0
  115. package/session-write-pi.mjs +130 -0
  116. package/session-write-residue.mjs +19 -0
  117. package/session-write-utils.mjs +77 -0
  118. package/session-write.d.ts +10 -0
  119. package/session-write.mjs +37 -0
  120. package/skill-toml.mjs +80 -0
  121. package/skills-control.mjs +193 -0
  122. package/skills-list.mjs +237 -0
  123. package/source-files.mjs +12 -2
  124. package/sqlite-reader.mjs +134 -18
  125. package/support-registry.json +402 -0
  126. package/support-report.mjs +69 -0
  127. package/trigger-records.mjs +77 -0
  128. package/triggers-read.mjs +67 -0
  129. package/workflow-read.mjs +165 -0
@@ -0,0 +1,77 @@
1
+ // Inbound-trigger facts from the complete owning-home read. Secret values do
2
+ // not cross this projection; authentication is only their recorded presence.
3
+ import { stringifyNativeJsonMember } from './native-json.mjs';
4
+ import { hermesDelivery } from './hermes-read-validation.mjs';
5
+ import { nativeTrim } from './text.mjs';
6
+ import { hermesYamlKeys } from './hermes-read-yaml.mjs';
7
+
8
+ const object = value => value != null && typeof value === 'object' && !Array.isArray(value);
9
+ const compare = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b));
10
+ const text = (row, key) => typeof row?.[key] === 'string' && row[key] ? row[key] : null;
11
+ function scalar(row, key) {
12
+ const value = row?.[key];
13
+ return typeof value === 'string' ? value : typeof value === 'number' || typeof value === 'boolean' ? stringifyNativeJsonMember(row, key) : null;
14
+ }
15
+ function webhook(name, events, delivery, raw, authenticated, description) {
16
+ const deliver = { target: delivery == null ? 'log' : delivery.kind === 'explicit' ? delivery.platform : delivery.kind };
17
+ const chat = (delivery?.kind === 'explicit' ? delivery.chat_id : null) ?? (scalar(raw, 'deliver_chat_id') || null);
18
+ if (chat != null) deliver.chat_id = chat;
19
+ const result = { name, kind: 'webhook', deliver, enabled: typeof raw.enabled === 'boolean' ? raw.enabled : raw.enabled !== 'false', authenticated };
20
+ if (events.length) result.events = events;
21
+ if (description != null) result.description = description;
22
+ return result;
23
+ }
24
+ export function folderTriggerRecords(subscriptions, config) {
25
+ const dynamic = Object.keys(subscriptions).sort(compare).map(name => {
26
+ const raw = subscriptions[name], events = Array.isArray(raw.events) ? raw.events.map((value, at) => typeof value === 'string' ? value : stringifyNativeJsonMember(raw.events, String(at))) : [];
27
+ return webhook(name, events, hermesDelivery('', name, raw.deliver, raw.deliver_extra), raw, typeof raw.secret === 'string', raw.description);
28
+ });
29
+ // The channel codec flattens only its top-level extra object, in source
30
+ // member order; a placeholder enters credentials rather than extras.
31
+ const channel = config.platforms?.webhook;
32
+ let routes;
33
+ const extra = value => { if (!(typeof value === 'string' && /^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/u.test(value))) routes = value; };
34
+ if (object(channel)) for (const key of hermesYamlKeys(channel)) {
35
+ const value = channel[key];
36
+ if (key === 'extra' && object(value) && Object.hasOwn(value, 'routes')) extra(value.routes);
37
+ else if (key === 'extra.routes') extra(value);
38
+ }
39
+ const fixed = object(routes) ? hermesYamlKeys(routes).flatMap(name => {
40
+ const raw = routes[name]; if (!object(raw)) return [];
41
+ const events = Array.isArray(raw.events) ? raw.events.flatMap((_, at) => { const word = scalar(raw.events, String(at)); return word == null ? [] : [word]; })
42
+ : typeof raw.events === 'string' ? raw.events.replace(/^[\[\]]+|[\[\]]+$/gu, '').split(',').map(word => nativeTrim(word).replace(/^["']+|["']+$/gu, '')).filter(Boolean) : [];
43
+ const word = scalar(raw, 'deliver') || null;
44
+ const delivery = word == null ? null : { kind: 'explicit', platform: word, chat_id: scalar(raw.deliver_extra, 'chat_id') || null };
45
+ return [webhook(name, events, delivery, raw, Object.hasOwn(raw, 'secret'), scalar(raw, 'description'))];
46
+ }) : [];
47
+ return { dynamic, fixed };
48
+ }
49
+ export function openclawTriggerRecords(config, profiles, defaultAgent) {
50
+ const hooks = config.hooks; if (!object(hooks)) return null;
51
+ const enabled = typeof hooks.enabled === 'boolean' ? hooks.enabled : false;
52
+ const authenticated = typeof hooks.token === 'string' || Object.hasOwn(hooks, 'tokenFile');
53
+ const base = (text(hooks, 'path') ?? '/hooks').replace(/\/+$/u, '');
54
+ const perProfile = new Map([...profiles.keys()].sort(compare).map(name => [name, new Map()]));
55
+ if (Array.isArray(hooks.mappings)) hooks.mappings.forEach((raw, index) => {
56
+ if (!object(raw)) return;
57
+ const id = text(raw, 'id') ?? `hook-${index}`;
58
+ const owner = raw.agentId === defaultAgent ? 'default' : typeof raw.agentId === 'string' ? raw.agentId : 'default';
59
+ const selected = perProfile.get(owner) ?? perProfile.get('default');
60
+ const match = object(raw.match) ? raw.match : {}, path = text(match, 'path');
61
+ const name = text(raw, 'id') ?? path?.replace(/^\/+/, '') ?? `mapping-${index}`;
62
+ const deliver = {}, target = {}, events = [];
63
+ for (const [wire, field] of [['action', 'action'], ['profile', 'agentId'], ['session_key', 'sessionKey'], ['wake_mode', 'wakeMode'], ['model', 'model']]) { const value = text(raw, field); if (value != null) target[wire] = value; }
64
+ if (typeof raw.deliver === 'string') {
65
+ deliver.target = raw.deliver;
66
+ if (typeof raw.to === 'string' || typeof raw.to === 'number') deliver.chat_id = scalar(raw, 'to');
67
+ } else { const value = text(raw, 'channel'); if (value != null) deliver.target = value; }
68
+ for (const key of ['source', 'event']) { const value = text(match, key); if (value != null) events.push(`${key}=${value}`); }
69
+ const row = { name, kind: 'hook_mapping', route: `${base}/${path?.replace(/^\/+/, '') ?? name}`, target, deliver,
70
+ enabled: enabled && (typeof raw.enabled === 'boolean' ? raw.enabled : true), authenticated };
71
+ if (events.length) row.events = events;
72
+ const description = text(raw, 'description'); if (description != null) row.description = description;
73
+ selected.set(id, { index, row });
74
+ });
75
+ const mappings = [...perProfile.values()].flatMap(subs => [...subs.keys()].sort(compare).map(name => subs.get(name))).sort((a, b) => a.index - b.index).map(value => value.row);
76
+ return { enabled, authenticated, base, mappings };
77
+ }
@@ -0,0 +1,67 @@
1
+ // Recorded inbound endpoints after complete home validity. No endpoint is
2
+ // opened, resolved or modified by this reader.
3
+ import { isAbsolute } from 'node:path';
4
+ import { resolveHarnessHomes } from './homes.mjs';
5
+ import { profilePathJoin as join, profilePathParent as parent } from './profile-paths.mjs';
6
+ import { readTriggerFolderHome } from './hermes-read-home.mjs';
7
+ import { readOpenclawTriggerHome } from './openclaw-profile-home.mjs';
8
+ import { nativeDuplicateObjectKeys, parseNativeJson, stringifyNativeJsonMember } from './native-json.mjs';
9
+
10
+ export const TRIGGER_HARNESSES = Object.freeze(['hermes', 'openclaw', 'orchestrator']);
11
+ const object = value => value != null && typeof value === 'object' && !Array.isArray(value);
12
+ const compare = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b));
13
+ const refuse = (message, rpcCode = -32602) => { throw Object.assign(new Error(message), { rpcCode }); };
14
+ const filesystem = (path, cwd) => !path.length || isAbsolute(path) || process.platform === 'win32' && /^[A-Za-z]:/u.test(path) ? path : join(cwd, path);
15
+ function struct(value, fields, name) {
16
+ if (Array.isArray(value)) {
17
+ if (value.length > fields.length) refuse(`${name} has too many fields`);
18
+ return parseNativeJson(`{${value.map((_, at) => `${JSON.stringify(fields[at])}:${stringifyNativeJsonMember(value, String(at))}`).join(',')}}`);
19
+ }
20
+ if (!object(value)) refuse(`${name} must be a struct`);
21
+ if (nativeDuplicateObjectKeys(value).some(key => fields.includes(key))) refuse(`${name} has a duplicate field`);
22
+ return value;
23
+ }
24
+ function query(params, env) {
25
+ const selected = struct(params, ['harness', 'homes'], 'triggers params');
26
+ if (selected.harness != null && typeof selected.harness !== 'string') refuse('harness must be a string');
27
+ const homes = resolveHarnessHomes(env);
28
+ if (Object.hasOwn(selected, 'homes')) {
29
+ const supplied = struct(selected.homes, Object.keys(homes), 'homes');
30
+ for (const key of Object.keys(homes)) if (Object.hasOwn(supplied, key)) {
31
+ if (typeof supplied[key] !== 'string') refuse(`homes.${key} must be a string`);
32
+ homes[key] = supplied[key];
33
+ }
34
+ }
35
+ if (selected.harness != null && !TRIGGER_HARNESSES.includes(selected.harness)) refuse(`\`${selected.harness}\` has no inbound-trigger store Volter Harness reads; \`triggers.list\` is supported for: ${TRIGGER_HARNESSES.join(', ')}`, -32020);
36
+ return { harness: selected.harness ?? null, homes };
37
+ }
38
+ function folderRows(harness, supplied, cwd) {
39
+ const home = harness === 'hermes' ? parent(supplied) ?? '.' : supplied;
40
+ let profiles; try { profiles = readTriggerFolderHome(filesystem(home, cwd), harness); } catch { return []; }
41
+ const rows = [];
42
+ for (const name of [...profiles.keys()].filter(name => harness !== 'hermes' || name === 'default').sort((a, b) => Number(a !== 'default') - Number(b !== 'default') || compare(a, b))) {
43
+ const dir = name === 'default' ? home : join(join(home, 'profiles'), name), selected = profiles.get(name);
44
+ for (const [records, file] of [[selected.dynamic, 'webhook_subscriptions.json'], [selected.fixed, 'config.yaml']]) for (const record of records) {
45
+ const target = { action: 'background' }; if (name !== 'default') target.profile = name;
46
+ rows.push({ ...record, harness, route: `${name === 'default' ? '' : `/p/${name}`}/webhooks/${record.name}`, target, source: join(dir, file) });
47
+ }
48
+ }
49
+ return rows;
50
+ }
51
+ function openclawRows(home, cwd) {
52
+ let hooks; try { hooks = readOpenclawTriggerHome(filesystem(home, cwd)); } catch { return []; }
53
+ if (hooks == null) return [];
54
+ const { base, enabled, authenticated } = hooks, source = join(home, 'openclaw.json');
55
+ const builtin = (name, session_key, description) => ({ name, harness: 'openclaw', kind: `builtin_${name}`, route: `${base}/${name}`, target: { action: name, session_key }, deliver: {}, enabled, authenticated, source, description });
56
+ return [builtin('wake', 'main', 'built-in: enqueue a system event into the main session'), builtin('agent', 'isolated', 'built-in: run an isolated agent turn'),
57
+ ...hooks.mappings.map(row => ({ ...row, harness: 'openclaw', source }))];
58
+ }
59
+ export function listTriggers(params, { env = process.env, cwd = process.cwd() } = {}) {
60
+ const selected = query(params, env);
61
+ return { schema: 'supercode.triggers.v1', triggers: TRIGGER_HARNESSES.flatMap(harness => selected.harness != null && selected.harness !== harness ? []
62
+ : harness === 'openclaw' ? openclawRows(selected.homes.openclaw, cwd) : folderRows(harness, selected.homes[harness], cwd)).map(row => ({
63
+ name: row.name, harness: row.harness, kind: row.kind, route: row.route, ...(row.events?.length ? { events: row.events } : {}),
64
+ target: row.target, deliver: row.deliver, enabled: row.enabled, authenticated: row.authenticated, source: row.source,
65
+ ...(row.description != null ? { description: row.description } : {}),
66
+ })) };
67
+ }
@@ -0,0 +1,165 @@
1
+ // The complete read-only board codec. Mutations and dispatch remain the
2
+ // owning harness's verbs; opening these tables creates no board or index.
3
+ import { readdirSync, statSync } from 'node:fs';
4
+ import { isAbsolute } from 'node:path';
5
+ import { childPath } from './session-spend-files.mjs';
6
+ import { profilePathJoin as join } from './profile-paths.mjs';
7
+ import { hermesTableExists, readHermesRows } from './hermes-read-store.mjs';
8
+ import { nativeDuplicateObjectKeys, nativeInteger, parseNativeJson, setNativeFloat, setNativeInteger, stringifyNativeJson, stringifyNativeJsonMember } from './native-json.mjs';
9
+ import { nativeTrim } from './text.mjs';
10
+
11
+ const minimum = -(1n << 63n), maximum = (1n << 63n) - 1n;
12
+ const utf8 = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
13
+ const compare = (left, right) => Buffer.compare(Buffer.from(left), Buffer.from(right));
14
+ const object = value => value != null && typeof value === 'object' && !Array.isArray(value);
15
+ const file = path => { try { return statSync(path).isFile(); } catch { return false; } };
16
+ const invalid = message => { throw Object.assign(new Error(message), { rpcCode: -32602 }); };
17
+ const filesystem = (path, cwd) => isAbsolute(path) || process.platform === 'win32' && /^[A-Za-z]:/u.test(path) ? path : childPath(cwd, path);
18
+ const taskColumns = new Set(['id', 'title', 'body', 'assignee', 'status', 'priority', 'created_by', 'created_at', 'started_at', 'completed_at', 'workspace_kind', 'workspace_path', 'branch_name', 'tenant', 'idempotency_key', 'result', 'skills', 'model_override', 'provider_override']);
19
+ const lanes = new Set(['triage', 'todo', 'scheduled', 'ready', 'running', 'blocked', 'review', 'done', 'archived']);
20
+
21
+ function params(value) {
22
+ if (Array.isArray(value)) {
23
+ if (value.length > 2) invalid('workflow load params have too many fields');
24
+ value = parseNativeJson(`{${value.map((_, at) => `${JSON.stringify(['from', 'home'][at])}:${stringifyNativeJsonMember(value, String(at))}`).join(',')}}`);
25
+ }
26
+ if (!object(value)) invalid('workflow load params must be a struct');
27
+ if (nativeDuplicateObjectKeys(value).some(key => ['from', 'home'].includes(key))) invalid('workflow load params have a duplicate field');
28
+ if (typeof value.home !== 'string') invalid('home must be a string');
29
+ if (value.from != null && !['hermes', 'supercode'].includes(value.from)) invalid('from must be hermes or supercode');
30
+ return { home: value.home, from: value.from ?? null };
31
+ }
32
+ function text(row, key) {
33
+ const value = row[key];
34
+ return typeof value === 'string' ? value.length ? value : null : typeof value === 'number' ? stringifyNativeJsonMember(row, key) : null;
35
+ }
36
+ function integer(row, key) {
37
+ const value = row[key];
38
+ if (typeof value === 'number') return nativeInteger(row, key, { signed: true });
39
+ if (typeof value !== 'string' || !/^[+-]?[0-9]+$/u.test(value)) return null;
40
+ const exact = BigInt(value); return exact < minimum || exact > maximum ? null : exact;
41
+ }
42
+ function sortedJson(value, parent, key) {
43
+ if (Array.isArray(value)) return `[${value.map((child, at) => sortedJson(child, value, String(at))).join(',')}]`;
44
+ if (object(value)) return `{${Object.keys(value).sort(compare).map(name => `${JSON.stringify(name)}:${sortedJson(value[name], value, name)}`).join(',')}}`;
45
+ return parent === undefined ? stringifyNativeJson(value) : stringifyNativeJsonMember(parent, key);
46
+ }
47
+ function jsonValue(row, key) {
48
+ const value = text(row, key); if (value == null) return undefined;
49
+ try {
50
+ const parsed = parseNativeJson(value);
51
+ // A shallow numeric box retains the scalar token. Object parsing keeps
52
+ // the native depth boundary rather than spending it on a wrapper.
53
+ return typeof parsed === 'number' ? parseNativeJson(`{"value":${value}}`) : { value: parseNativeJson(sortedJson(parsed)) };
54
+ } catch { return undefined; }
55
+ }
56
+ const jsonText = (row, key) => jsonValue(row, key)?.value;
57
+
58
+ // sidecar::ms_to_rfc3339: saturating seconds-to-ms conversion, Euclidean
59
+ // remainder and negative whole seconds clamped to epoch. BigInt also keeps
60
+ // calendar years beyond JavaScript Date's range rather than rounding them.
61
+ function at(row, key) {
62
+ const seconds = integer(row, key); if (seconds == null) return null;
63
+ const product = seconds * 1000n, ms = product < minimum ? minimum : product > maximum ? maximum : product;
64
+ const millis = (ms % 1000n + 1000n) % 1000n;
65
+ const secs = ms < 0n ? 0n : ms / 1000n, days = secs / 86400n, rem = secs % 86400n;
66
+ const z = days + 719468n, era = z / 146097n, doe = z - era * 146097n;
67
+ const yoe = (doe - doe / 1460n + doe / 36524n - doe / 146096n) / 365n;
68
+ const yearBase = yoe + era * 400n, doy = doe - (365n * yoe + yoe / 4n - yoe / 100n);
69
+ const mp = (5n * doy + 2n) / 153n, day = doy - (153n * mp + 2n) / 5n + 1n;
70
+ const month = mp < 10n ? mp + 3n : mp - 9n, year = month <= 2n ? yearBase + 1n : yearBase;
71
+ const pad = (value, width = 2) => String(value).padStart(width, '0');
72
+ return `${pad(year, 4)}-${pad(month)}-${pad(day)}T${pad(rem / 3600n)}:${pad(rem % 3600n / 60n)}:${pad(rem % 60n)}.${pad(millis, 3)}Z`;
73
+ }
74
+ function skills(row) {
75
+ const value = jsonText(row, 'skills');
76
+ return Array.isArray(value) ? value.filter(item => typeof item === 'string') : (text(row, 'skills') ?? '').split(',').map(nativeTrim).filter(Boolean);
77
+ }
78
+ const ordered = entries => parseNativeJson(`{${[...entries].sort(([left], [right]) => compare(left, right)).map(([key, value]) => `${JSON.stringify(key)}:${stringifyNativeJson(value)}`).join(',')}}`);
79
+ function task(row) {
80
+ const id = text(row, 'id'); if (id == null) return null;
81
+ const status = text(row, 'status') ?? '', lane = lanes.has(status) ? status : 'unknown';
82
+ const recordedKind = text(row, 'workspace_kind'), kind = recordedKind == null ? 'scratch' : ['scratch', 'dir', 'worktree'].includes(recordedKind) ? recordedKind : 'unknown';
83
+ const kept = new Map(Object.keys(row).filter(key => !taskColumns.has(key) && row[key] != null).map(key => [key, stringifyNativeJsonMember(row, key)]));
84
+ if (lane === 'unknown') kept.set('status', JSON.stringify(status));
85
+ if (kind === 'unknown') kept.set('workspace_kind', stringifyNativeJsonMember(row, 'workspace_kind'));
86
+ const residue = parseNativeJson(`{${[...kept].sort(([left], [right]) => compare(left, right)).map(([key, encoded]) => `${JSON.stringify(key)}:${encoded}`).join(',')}}`);
87
+ const result = { id, title: text(row, 'title') ?? '', body: text(row, 'body'), assignee: text(row, 'assignee'), machine: null, subtask_of: null, lane, priority: 0,
88
+ tenant: text(row, 'tenant'), idempotency_key: text(row, 'idempotency_key'), workspace: { kind, path: text(row, 'workspace_path'), branch: text(row, 'branch_name') }, skills: skills(row),
89
+ model: text(row, 'model_override'), provider: text(row, 'provider_override'), created_by: text(row, 'created_by'), created_at: at(row, 'created_at'), started_at: at(row, 'started_at'), completed_at: at(row, 'completed_at'),
90
+ result: text(row, 'result'), attempts: [], reviews: [], comments: [], residue };
91
+ setNativeInteger(result, 'priority', integer(row, 'priority') ?? 0n); return result;
92
+ }
93
+ function attemptSession(value) {
94
+ const field = key => typeof value?.[key] === 'string' ? value[key] : null;
95
+ const result = { surface: field('surface'), id: field('session_id'), harness: field('harness'), machine: field('machine'), pane: field('pane'), address: field('address'), mode: field('mode'), pid: null };
96
+ const pid = nativeInteger(value, 'pid', { signed: true }); if (pid != null) setNativeInteger(result, 'pid', pid); return result;
97
+ }
98
+ function attempt(row) {
99
+ const id = text(row, 'id'); if (id == null) return null;
100
+ const summary = text(row, 'summary'), box = jsonValue(row, 'metadata'); let metadata = box?.value, session = null;
101
+ if (object(metadata) && Object.hasOwn(metadata, 'supercode')) { session = attemptSession(metadata.supercode); delete metadata.supercode; }
102
+ if (object(metadata) && !Object.keys(metadata).length) metadata = undefined;
103
+ const handoff = summary != null || metadata !== undefined ? { summary, metadata: metadata ?? null } : null;
104
+ if (handoff && typeof metadata === 'number') {
105
+ const exact = nativeInteger(box, 'value');
106
+ if (exact == null) setNativeFloat(handoff, 'metadata', metadata); else setNativeInteger(handoff, 'metadata', exact);
107
+ }
108
+ return { id, profile: text(row, 'profile'), step: text(row, 'step_key'), status: text(row, 'status') ?? '', started_at: at(row, 'started_at'), ended_at: at(row, 'ended_at'),
109
+ outcome: text(row, 'outcome'), handoff, error: text(row, 'error'), session };
110
+ }
111
+ function review(row) {
112
+ const verdicts = { review_requested: 'requested', approved: 'approved', review_approved: 'approved', changes_requested: 'changes_requested', escalated: 'escalated' };
113
+ const kind = text(row, 'kind'), verdict = Object.hasOwn(verdicts, kind) ? verdicts[kind] : null;
114
+ if (verdict == null) return null;
115
+ const payload = jsonText(row, 'payload'), field = key => typeof payload?.[key] === 'string' ? payload[key] : null;
116
+ return { verdict, by: field('reviewer') ?? field('by') ?? field('profile'), reason: field('reason'), at: at(row, 'created_at') };
117
+ }
118
+ const rows = (db, table, sql) => hermesTableExists(db, table) ? readHermesRows(db, sql) ?? [] : [];
119
+ function board(slug, root, db) {
120
+ const tasks = new Map();
121
+ for (const row of rows(db, 'tasks', 'SELECT * FROM tasks')) { const value = task(row); if (value) tasks.set(value.id, value); }
122
+ for (const row of rows(db, 'supercode_cards', 'SELECT * FROM supercode_cards')) {
123
+ const value = tasks.get(text(row, 'task_id')); if (value) { value.machine = text(row, 'machine'); value.subtask_of = text(row, 'subtask_of'); }
124
+ }
125
+ for (const row of rows(db, 'task_runs', 'SELECT * FROM task_runs ORDER BY id')) {
126
+ const value = tasks.get(text(row, 'task_id')), run = attempt(row); if (value && run) value.attempts.push(run);
127
+ }
128
+ for (const row of rows(db, 'task_events', 'SELECT task_id, kind, payload, created_at FROM task_events ORDER BY id')) {
129
+ const value = tasks.get(text(row, 'task_id')), event = review(row); if (value && event) value.reviews.push(event);
130
+ }
131
+ for (const row of rows(db, 'task_comments', 'SELECT task_id, author, body, created_at FROM task_comments ORDER BY id')) {
132
+ const value = tasks.get(text(row, 'task_id')), author = text(row, 'author'), body = text(row, 'body');
133
+ if (value && author != null && body != null) value.comments.push({ author, body, at: at(row, 'created_at') });
134
+ }
135
+ const dependencies = rows(db, 'task_links', 'SELECT parent_id, child_id FROM task_links ORDER BY parent_id, child_id').flatMap(row => {
136
+ const parent = text(row, 'parent_id'), child = text(row, 'child_id'); return parent != null && child != null ? [{ parent, child }] : [];
137
+ });
138
+ return { slug, name: null, root, tasks: ordered(tasks), dependencies };
139
+ }
140
+ function named(home) {
141
+ const root = join(join(home, 'kanban'), 'boards'); let entries;
142
+ try { entries = readdirSync(root, { encoding: 'buffer' }); } catch { return []; }
143
+ return entries.sort(Buffer.compare).flatMap(entry => { try { const slug = utf8.decode(entry); return slug && !slug.startsWith('_') ? [{ slug, path: childPath(root, entry) }] : []; } catch { return []; } });
144
+ }
145
+ function declaresSupercode(home) {
146
+ if (file(join(join(home, 'kanban'), 'ztrack.json'))) return true;
147
+ const boards = join(join(home, 'kanban'), 'boards');
148
+ try { return readdirSync(boards, { encoding: 'buffer' }).some(entry => file(childPath(childPath(boards, entry), 'ztrack.json'))); }
149
+ catch { return false; }
150
+ }
151
+
152
+ export function loadWorkflow(paramsValue, context = {}) {
153
+ const selected = params(paramsValue), home = filesystem(selected.home, context.cwd ?? process.cwd());
154
+ let from = selected.from;
155
+ if (from == null) from = declaresSupercode(home) || !file(join(home, 'kanban.db')) ? 'supercode' : 'hermes';
156
+ const boards = new Map(), name = from === 'hermes' ? 'kanban.db' : 'ztrack.json';
157
+ const defaultFile = from === 'hermes' ? join(home, name) : join(join(home, 'kanban'), name);
158
+ if (file(defaultFile)) boards.set('default', board('default', selected.home, from === 'hermes' ? defaultFile : `${defaultFile}.board.db`));
159
+ for (const entry of named(home)) {
160
+ const marker = childPath(entry.path, name); if (!file(marker)) continue;
161
+ const displayed = join(join(join(selected.home, 'kanban'), 'boards'), entry.slug);
162
+ boards.set(entry.slug, board(entry.slug, displayed, from === 'hermes' ? marker : childPath(entry.path, `${name}.board.db`)));
163
+ }
164
+ return { workflow: { root: selected.home, boards: ordered(boards) } };
165
+ }