maka-agent 0.2.0-dev.22.20260905 → 0.2.0-dev.23.20260906

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 (63) hide show
  1. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  2. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  3. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  4. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  5. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  6. package/node_modules/@maka/core/dist/bot-events.js +18 -63
  7. package/node_modules/@maka/core/dist/model-call-attempt.js +0 -82
  8. package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -186
  9. package/node_modules/@maka/core/dist/record-schema.js +13 -0
  10. package/node_modules/@maka/core/dist/redaction.js +41 -58
  11. package/node_modules/@maka/core/dist/session-trace.js +5 -1
  12. package/node_modules/@maka/core/dist/usage-ledger-merge.js +7 -9
  13. package/node_modules/@maka/core/dist/workhub-creation-intent.js +34 -20
  14. package/node_modules/@maka/eval/harbor/egress-proxy/entrypoint.sh +2 -1
  15. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +51 -24
  16. package/node_modules/@maka/runtime/dist/bots/base-adapter.js +17 -0
  17. package/node_modules/@maka/runtime/dist/bots/bot-test.js +38 -22
  18. package/node_modules/@maka/runtime/dist/bots/dingtalk-bridge.js +7 -5
  19. package/node_modules/@maka/runtime/dist/bots/discord-bridge.js +4 -4
  20. package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +4 -5
  21. package/node_modules/@maka/runtime/dist/bots/qq-bridge.js +6 -4
  22. package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +3 -4
  23. package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +4 -5
  24. package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +17 -19
  25. package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +5 -6
  26. package/node_modules/@maka/runtime/dist/bots/ws-bridge-base.js +2 -3
  27. package/node_modules/@maka/runtime/dist/session-trace-projection.js +3 -30
  28. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +4 -1
  29. package/node_modules/@maka/runtime-host/dist/client/index.js +1 -1
  30. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +13 -0
  31. package/node_modules/@maka/runtime-host/dist/client/registered-host-termination.js +0 -59
  32. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +28 -16
  33. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +4 -0
  34. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +8 -0
  35. package/node_modules/@maka/runtime-host/dist/protocol/index.js +13 -1
  36. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
  37. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +28 -1
  38. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +33 -0
  39. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +26 -1
  40. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +5 -1
  41. package/node_modules/@maka/runtime-host/dist/server/canonical-usage-reader.js +24 -11
  42. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +7 -4
  43. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +70 -6
  44. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +6 -3
  45. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +12 -6
  46. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +9 -2
  47. package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +7 -0
  48. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +21 -0
  49. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +151 -5
  50. package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +4 -11
  51. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -15
  52. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +6 -1
  53. package/node_modules/@maka/storage/dist/agent-run-store.js +44 -0
  54. package/node_modules/@maka/storage/dist/conversation-operational-state.js +4 -0
  55. package/node_modules/@maka/storage/dist/execution-stores.js +2 -0
  56. package/node_modules/@maka/storage/dist/model-call-ledger.js +199 -32
  57. package/node_modules/@maka/storage/dist/model-call-usage-sql.js +136 -0
  58. package/node_modules/@maka/storage/dist/operational-state-store-public.js +1 -1
  59. package/node_modules/@maka/storage/dist/operational-state-store.js +5 -3
  60. package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +11 -1
  61. package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +200 -20
  62. package/node_modules/@maka/storage/dist/usage-stores.js +6 -2
  63. package/package.json +1 -1
@@ -16,7 +16,132 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
- export const SQLITE_USAGE_SCHEMA_VERSION = 5;
19
+ export const SQLITE_USAGE_SCHEMA_VERSION = 7;
20
+ /**
21
+ * The canonical ledger's columns, in the order every statement binds them.
22
+ *
23
+ * Ordered so `attempt_id, completed_at, session_id` — the three a damaged row
24
+ * keeps — come first, and the pricing columns follow.
25
+ */
26
+ export const MODEL_CALL_COLUMNS = [
27
+ 'attempt_id',
28
+ 'completed_at',
29
+ 'session_id',
30
+ 'logical_call_id',
31
+ 'turn_id',
32
+ 'call_kind',
33
+ 'connection_slug',
34
+ 'provider_id',
35
+ 'model_id',
36
+ 'latency_ms',
37
+ 'status',
38
+ 'error_class',
39
+ 'usage_basis',
40
+ 'input_tokens',
41
+ 'output_tokens',
42
+ 'cache_read_input_tokens',
43
+ 'cache_miss_input_tokens',
44
+ 'cache_write_input_tokens',
45
+ 'reasoning_tokens',
46
+ 'cost_basis',
47
+ 'cost_usd',
48
+ ];
49
+ /**
50
+ * The JSON path each pricing column was read from before the columns existed,
51
+ * used once by the migration that converted the rows.
52
+ */
53
+ const MODEL_CALL_COLUMN_SOURCES = [
54
+ ['logical_call_id', '$.logicalCallId'],
55
+ ['turn_id', '$.turnId'],
56
+ ['call_kind', '$.callKind'],
57
+ ['connection_slug', '$.connectionSlug'],
58
+ ['provider_id', '$.providerId'],
59
+ ['model_id', '$.modelId'],
60
+ ['latency_ms', '$.latencyMs'],
61
+ ['status', '$.status'],
62
+ ['error_class', '$.errorClass'],
63
+ ['usage_basis', '$.usageBasis'],
64
+ ['input_tokens', '$.inputTokens'],
65
+ ['output_tokens', '$.outputTokens'],
66
+ ['cache_read_input_tokens', '$.cacheReadInputTokens'],
67
+ ['cache_miss_input_tokens', '$.cacheMissInputTokens'],
68
+ ['cache_write_input_tokens', '$.cacheWriteInputTokens'],
69
+ ['reasoning_tokens', '$.reasoningTokens'],
70
+ ['cost_basis', '$.costBasis'],
71
+ ['cost_usd', '$.costUsd'],
72
+ ];
73
+ /** The columns that are present together or not at all. See the table's CHECK. */
74
+ const MODEL_CALL_REQUIRED_COLUMNS = [
75
+ 'logical_call_id',
76
+ 'turn_id',
77
+ 'call_kind',
78
+ 'provider_id',
79
+ 'model_id',
80
+ 'latency_ms',
81
+ 'status',
82
+ 'usage_basis',
83
+ 'cost_basis',
84
+ ];
85
+ const MODEL_CALL_TOKEN_COLUMNS = [
86
+ 'input_tokens',
87
+ 'output_tokens',
88
+ 'cache_read_input_tokens',
89
+ 'cache_miss_input_tokens',
90
+ 'cache_write_input_tokens',
91
+ 'reasoning_tokens',
92
+ ];
93
+ const NO_TOKENS = MODEL_CALL_TOKEN_COLUMNS.map((column) => `${column} IS NULL`).join(' AND ');
94
+ /**
95
+ * Canonical model-call accounting ledger (#1679).
96
+ *
97
+ * One column per field a cost answer reads, so the totals are a `SUM` the
98
+ * database can compute rather than every row of a workspace's history parsed
99
+ * into memory first.
100
+ *
101
+ * A row is either a complete pricing record or a tombstone that kept only the
102
+ * identity and timestamp of a call whose stored form was damaged. Never half of
103
+ * each — which is what makes `cost_basis IS NOT NULL` a sound test for "this row
104
+ * can be counted", and its negation the count a read reports as unreadable.
105
+ *
106
+ * The vocabularies (`status`, `call_kind`) are deliberately not constrained
107
+ * here. They are already validated where a record is decoded, and a CHECK on
108
+ * them would turn one damaged row into a failed migration for the whole
109
+ * workspace.
110
+ */
111
+ const MODEL_CALL_TABLE = `
112
+ CREATE TABLE IF NOT EXISTS %TABLE% (
113
+ attempt_id TEXT PRIMARY KEY,
114
+ completed_at INTEGER NOT NULL CHECK (completed_at >= 0),
115
+ session_id TEXT,
116
+ logical_call_id TEXT,
117
+ turn_id TEXT,
118
+ call_kind TEXT,
119
+ connection_slug TEXT,
120
+ provider_id TEXT,
121
+ model_id TEXT,
122
+ latency_ms INTEGER,
123
+ status TEXT,
124
+ error_class TEXT,
125
+ usage_basis TEXT,
126
+ input_tokens INTEGER,
127
+ output_tokens INTEGER,
128
+ cache_read_input_tokens INTEGER,
129
+ cache_miss_input_tokens INTEGER,
130
+ cache_write_input_tokens INTEGER,
131
+ reasoning_tokens INTEGER,
132
+ cost_basis TEXT,
133
+ cost_usd REAL,
134
+ CHECK (${MODEL_CALL_REQUIRED_COLUMNS.map((column) => `(${column} IS NULL) = (cost_basis IS NULL)`).join(' AND ')}),
135
+ -- A price that could not be resolved must never surface as an amount, and a
136
+ -- priced call must carry one. Zero stays legal: it is the only way to say a
137
+ -- call was genuinely free.
138
+ CHECK (cost_basis IS NOT 'priced' OR cost_usd IS NOT NULL),
139
+ CHECK (cost_basis IS NOT 'unpriced' OR cost_usd IS NULL),
140
+ -- "The provider reported no usage" and "it reported zero" are different
141
+ -- facts, so a missing-usage row carries no token counts at all.
142
+ CHECK (usage_basis IS NOT 'missing' OR (${NO_TOKENS}))
143
+ )
144
+ `;
20
145
  export function migrateSqliteUsageDatabase(db) {
21
146
  db.exec(`
22
147
  CREATE TABLE IF NOT EXISTS usage_llm_calls (
@@ -40,18 +165,7 @@ export function migrateSqliteUsageDatabase(db) {
40
165
  CREATE INDEX IF NOT EXISTS usage_tool_invocations_ts
41
166
  ON usage_tool_invocations(ts DESC, id);
42
167
 
43
- -- Canonical model-call accounting ledger (#1679). Separate from
44
- -- usage_llm_calls, which is a frozen historical projection: these rows carry
45
- -- usageBasis/costBasis, which that schema cannot express.
46
- CREATE TABLE IF NOT EXISTS usage_model_call_attempts (
47
- attempt_id TEXT PRIMARY KEY,
48
- completed_at INTEGER NOT NULL CHECK (completed_at >= 0),
49
- record_json TEXT NOT NULL,
50
- session_id TEXT
51
- );
52
-
53
- CREATE INDEX IF NOT EXISTS usage_model_call_attempts_completed_at
54
- ON usage_model_call_attempts(completed_at DESC, attempt_id);
168
+ ${MODEL_CALL_TABLE.replace('%TABLE%', 'usage_model_call_attempts')};
55
169
 
56
170
  -- The AgentRun sequence is the projection's sole progress authority. A run
57
171
  -- is behind exactly when its latest model-call event is newer than this
@@ -82,26 +196,92 @@ export function migrateSqliteUsageDatabase(db) {
82
196
  `);
83
197
  db.exec('DROP TABLE IF EXISTS usage_model_call_reprojection');
84
198
  ensureColumn(db, 'usage_llm_calls', 'session_id', 'TEXT');
85
- ensureColumn(db, 'usage_model_call_attempts', 'session_id', 'TEXT');
86
199
  db.exec(`
87
200
  UPDATE usage_llm_calls
88
201
  SET session_id = json_extract(record_json, '$.sessionId')
89
202
  WHERE session_id IS NULL AND json_valid(record_json);
90
203
 
91
- UPDATE usage_model_call_attempts
92
- SET session_id = json_extract(record_json, '$.sessionId')
93
- WHERE session_id IS NULL AND json_valid(record_json);
94
-
95
204
  CREATE INDEX IF NOT EXISTS usage_llm_calls_session_ts
96
205
  ON usage_llm_calls(session_id, ts DESC, id);
206
+ `);
207
+ // A ledger old enough to predate Session attribution has no column to carry
208
+ // through, and the conversion below reads one.
209
+ ensureColumn(db, 'usage_model_call_attempts', 'session_id', 'TEXT');
210
+ spreadModelCallRecordJson(db);
211
+ db.exec(`
212
+ CREATE INDEX IF NOT EXISTS usage_model_call_attempts_completed_at
213
+ ON usage_model_call_attempts(completed_at DESC, attempt_id);
97
214
 
98
215
  CREATE INDEX IF NOT EXISTS usage_model_call_attempts_session_completed_at
99
216
  ON usage_model_call_attempts(session_id, completed_at DESC, attempt_id);
100
217
  `);
101
218
  }
219
+ /**
220
+ * Converts rows that stored the record as one JSON blob into the columns.
221
+ *
222
+ * Not rebuilt from the AgentRun authority, the usual move for a read model:
223
+ * deleting a Session drops its runs and cascades their events while these rows
224
+ * are kept on purpose, so for those calls this table is the last copy and a
225
+ * wipe-and-replay would erase their spend. See the header of
226
+ * `model-call-ledger.ts`.
227
+ *
228
+ * A blob that does not yield a whole record — damaged text, or a row some other
229
+ * schema left behind — keeps its identity and timestamp and loses the rest. That
230
+ * is the same claim the old JSON reader made by counting it as unreadable, and
231
+ * it is why the conversion is all-or-nothing per row: a half-filled row would
232
+ * make the table's own CHECK unsatisfiable and take the whole migration with it.
233
+ */
234
+ function spreadModelCallRecordJson(db) {
235
+ if (!hasColumn(db, 'usage_model_call_attempts', 'record_json'))
236
+ return;
237
+ const extracted = MODEL_CALL_COLUMN_SOURCES.map(([column, path]) => `CASE WHEN json_valid(record_json) THEN json_extract(record_json, '${path}') END AS ${column}`).join(',\n ');
238
+ const readable = [
239
+ ...MODEL_CALL_REQUIRED_COLUMNS.map((column) => `${column} IS NOT NULL`),
240
+ "(cost_basis IS NOT 'priced' OR cost_usd IS NOT NULL)",
241
+ "(cost_basis IS NOT 'unpriced' OR cost_usd IS NULL)",
242
+ `(usage_basis IS NOT 'missing' OR (${NO_TOKENS}))`,
243
+ ].join('\n AND ');
244
+ const pricing = MODEL_CALL_COLUMN_SOURCES.map(([column]) => `CASE WHEN readable THEN ${column} END`).join(',\n ');
245
+ // The old table moves aside so the new one is created under its final name:
246
+ // a table renamed into place keeps a rewritten `CREATE` statement, and the
247
+ // schema guard compares those texts.
248
+ db.exec(`
249
+ ALTER TABLE usage_model_call_attempts RENAME TO usage_model_call_attempts_blob;
250
+
251
+ ${MODEL_CALL_TABLE.replace('%TABLE%', 'usage_model_call_attempts')};
252
+
253
+ INSERT INTO usage_model_call_attempts(${MODEL_CALL_COLUMNS.join(', ')})
254
+ WITH extracted AS (
255
+ SELECT
256
+ attempt_id,
257
+ completed_at,
258
+ -- A tombstone keeps its Session: the row still says a call happened here.
259
+ COALESCE(
260
+ session_id,
261
+ CASE WHEN json_valid(record_json) THEN json_extract(record_json, '$.sessionId') END
262
+ ) AS session_id,
263
+ ${extracted}
264
+ FROM usage_model_call_attempts_blob
265
+ ),
266
+ classified AS (
267
+ SELECT *, (${readable}) AS readable FROM extracted
268
+ )
269
+ SELECT
270
+ attempt_id,
271
+ completed_at,
272
+ session_id,
273
+ ${pricing}
274
+ FROM classified;
275
+
276
+ DROP TABLE usage_model_call_attempts_blob;
277
+ `);
278
+ }
102
279
  function ensureColumn(db, table, column, definition) {
103
- const columns = db.prepare(`PRAGMA table_info(${table})`).all();
104
- if (columns.some((candidate) => candidate.name === column))
280
+ if (hasColumn(db, table, column))
105
281
  return;
106
282
  db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
107
283
  }
284
+ function hasColumn(db, table, column) {
285
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all();
286
+ return columns.some((candidate) => candidate.name === column);
287
+ }
@@ -291,7 +291,9 @@ function createWriterFacade(lease, telemetry, modelCalls, pricing) {
291
291
  recordToolInvocation: (record) => admitSessionUsageMutation(record.sessionId, () => telemetry.insertToolInvocation(record)),
292
292
  },
293
293
  modelCalls: {
294
- modelCallAttempts: (range, sessionId) => read(() => modelCalls.read(range, sessionId)),
294
+ modelCallSummary: (query, now) => read(() => modelCalls.summary(query, now)),
295
+ modelCallBuckets: (query, groupBy, now) => read(() => modelCalls.buckets(query, groupBy, now)),
296
+ modelCallLogs: (query, now, offset, limit) => read(() => modelCalls.logs(query, now, offset, limit)),
295
297
  catchUpModelCallProjection: admitModelCallProjectionCatchUp,
296
298
  },
297
299
  pricing: {
@@ -323,7 +325,9 @@ function telemetryReader(repo, run) {
323
325
  }
324
326
  function modelCallReader(ledger, run) {
325
327
  return Object.freeze({
326
- modelCallAttempts: (range, sessionId) => run(() => ledger.read(range, sessionId)),
328
+ modelCallSummary: (query, now) => run(() => ledger.summary(query, now)),
329
+ modelCallBuckets: (query, groupBy, now) => run(() => ledger.buckets(query, groupBy, now)),
330
+ modelCallLogs: (query, now, offset, limit) => run(() => ledger.logs(query, now, offset, limit)),
327
331
  });
328
332
  }
329
333
  function pricingReader(store, run) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maka-agent",
3
- "version": "0.2.0-dev.22.20260905",
3
+ "version": "0.2.0-dev.23.20260906",
4
4
  "description": "Apache Maka (Incubating) developer snapshot; not an Apache release.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",