maka-agent 0.2.1-dev.89.20261016 → 0.2.1-dev.90.20261017
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/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/event-wait.js +98 -2
- package/node_modules/@maka/runtime/dist/plugin-executor-backend.js +14 -7
- package/node_modules/@maka/runtime/dist/plugin-executor-service.js +6 -0
- package/node_modules/@maka/runtime-host/dist/server/builtin-external-agent-plugins.js +0 -19
- package/node_modules/@maka/runtime-host/dist/server/event-wait-adapter.js +47 -0
- package/node_modules/@maka/runtime-host/dist/server/event-wait-manager.js +1186 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +2 -3
- package/node_modules/@maka/storage/dist/event-wait-authority.js +132 -2
- package/node_modules/@maka/storage/dist/execution-stores.js +19 -1
- package/node_modules/@maka/storage/dist/sqlite-event-wait-authority.js +116 -1
- package/node_modules/@maka/storage/dist/sqlite-workflow-schema.js +16 -1
- package/node_modules/@maka/storage/package.json +1 -0
- package/package.json +1 -1
|
Binary file
|
|
@@ -29,7 +29,12 @@ export const EVENT_WAIT_LIMITS = Object.freeze({
|
|
|
29
29
|
evidenceRefs: 8,
|
|
30
30
|
evidenceRefBytes: 1024,
|
|
31
31
|
reasonBytes: 1024,
|
|
32
|
-
|
|
32
|
+
cursorBytes: 2048,
|
|
33
|
+
observationAttempts: 1_000_000,
|
|
34
|
+
// Every combination of bounded fields, including observation and a cancelled
|
|
35
|
+
// priorResolution, fits below 42 KiB even with JSON escaping. Leave room for
|
|
36
|
+
// the full lifecycle so an accepted result can always retain its facts on cancel.
|
|
37
|
+
recordBytes: 48 * 1024,
|
|
33
38
|
});
|
|
34
39
|
const encoder = new TextEncoder();
|
|
35
40
|
const invalidIdCharacterPattern = /[^A-Za-z0-9_-]/u;
|
|
@@ -187,7 +192,7 @@ export function decodeEventWaitRecord(value) {
|
|
|
187
192
|
'deadlineAt',
|
|
188
193
|
'status',
|
|
189
194
|
...lifecycleKeys,
|
|
190
|
-
], root.status === 'cancelled' ? ['priorResolution'] : []);
|
|
195
|
+
], root.status === 'cancelled' ? ['priorResolution', 'observation'] : ['observation']);
|
|
191
196
|
if (r.schemaVersion !== EVENT_WAIT_SCHEMA_VERSION)
|
|
192
197
|
invalid('schemaVersion');
|
|
193
198
|
const lease = shape(r.goalControlLease, ['goalId', 'generation']);
|
|
@@ -230,6 +235,62 @@ export function decodeEventWaitRecord(value) {
|
|
|
230
235
|
invalid('lifecycle time');
|
|
231
236
|
return result;
|
|
232
237
|
};
|
|
238
|
+
if (Object.hasOwn(r, 'observation')) {
|
|
239
|
+
const x = shape(r.observation, [
|
|
240
|
+
'schemaVersion',
|
|
241
|
+
'attempts',
|
|
242
|
+
'lastAttemptAt',
|
|
243
|
+
'lastObservedAt',
|
|
244
|
+
'nextObserveAt',
|
|
245
|
+
'nextPollAt',
|
|
246
|
+
'consecutiveErrors',
|
|
247
|
+
'outcome',
|
|
248
|
+
'cursor',
|
|
249
|
+
'eventKey',
|
|
250
|
+
'reason',
|
|
251
|
+
]);
|
|
252
|
+
const attempts = integer(x.attempts, 'observation attempts', 1);
|
|
253
|
+
const consecutiveErrors = integer(x.consecutiveErrors, 'consecutive errors');
|
|
254
|
+
const lastAttemptAt = time(x.lastAttemptAt);
|
|
255
|
+
const nextObserveAt = integer(x.nextObserveAt, 'next observation');
|
|
256
|
+
const nextPollAt = integer(x.nextPollAt, 'next poll');
|
|
257
|
+
const lastObservedAt = x.lastObservedAt === null ? null : time(x.lastObservedAt);
|
|
258
|
+
if (x.schemaVersion !== 1 ||
|
|
259
|
+
attempts > EVENT_WAIT_LIMITS.observationAttempts ||
|
|
260
|
+
consecutiveErrors > attempts ||
|
|
261
|
+
nextObserveAt < lastAttemptAt ||
|
|
262
|
+
nextPollAt < nextObserveAt ||
|
|
263
|
+
(x.outcome !== 'checking' && (lastObservedAt === null || lastObservedAt < lastAttemptAt)) ||
|
|
264
|
+
typeof x.outcome !== 'string' ||
|
|
265
|
+
![
|
|
266
|
+
'checking',
|
|
267
|
+
'pending',
|
|
268
|
+
'unknown',
|
|
269
|
+
'satisfied',
|
|
270
|
+
'invalidated',
|
|
271
|
+
'source_unavailable',
|
|
272
|
+
].includes(x.outcome))
|
|
273
|
+
invalid('observation');
|
|
274
|
+
Object.assign(base, {
|
|
275
|
+
observation: {
|
|
276
|
+
schemaVersion: 1,
|
|
277
|
+
attempts,
|
|
278
|
+
lastAttemptAt,
|
|
279
|
+
lastObservedAt,
|
|
280
|
+
nextObserveAt,
|
|
281
|
+
nextPollAt,
|
|
282
|
+
consecutiveErrors,
|
|
283
|
+
outcome: x.outcome,
|
|
284
|
+
cursor: x.cursor === null ? null : text(x.cursor, 'cursor', EVENT_WAIT_LIMITS.cursorBytes),
|
|
285
|
+
eventKey: x.eventKey === null
|
|
286
|
+
? null
|
|
287
|
+
: text(x.eventKey, 'event key', EVENT_WAIT_LIMITS.receiptKeyBytes),
|
|
288
|
+
reason: x.reason === null
|
|
289
|
+
? null
|
|
290
|
+
: text(x.reason, 'observation reason', EVENT_WAIT_LIMITS.reasonBytes),
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
233
294
|
const resolution = (v, resolvedAt) => {
|
|
234
295
|
const x = shape(v, ['outcome', 'receiptKey', 'observedAt', 'evidenceRefs']);
|
|
235
296
|
if (x.outcome !== 'satisfied' &&
|
|
@@ -306,7 +367,41 @@ export function assertEventWaitTransition(previous, next) {
|
|
|
306
367
|
}
|
|
307
368
|
if (b.updatedAt < a.updatedAt)
|
|
308
369
|
invalid('time regression');
|
|
370
|
+
if (a.observation) {
|
|
371
|
+
if (!b.observation ||
|
|
372
|
+
b.observation.attempts < a.observation.attempts ||
|
|
373
|
+
b.observation.attempts > a.observation.attempts + 1 ||
|
|
374
|
+
b.observation.lastAttemptAt < a.observation.lastAttemptAt ||
|
|
375
|
+
(a.observation.lastObservedAt !== null &&
|
|
376
|
+
(b.observation.lastObservedAt === null ||
|
|
377
|
+
b.observation.lastObservedAt < a.observation.lastObservedAt)))
|
|
378
|
+
invalid('observation regression');
|
|
379
|
+
if (b.observation.attempts === a.observation.attempts &&
|
|
380
|
+
b.observation.lastAttemptAt !== a.observation.lastAttemptAt)
|
|
381
|
+
invalid('attempt time without reservation');
|
|
382
|
+
if (b.observation.attempts > a.observation.attempts &&
|
|
383
|
+
(b.status !== 'waiting' ||
|
|
384
|
+
b.observation.outcome !== 'checking' ||
|
|
385
|
+
b.observation.lastObservedAt !== a.observation.lastObservedAt ||
|
|
386
|
+
b.observation.cursor !== a.observation.cursor ||
|
|
387
|
+
b.observation.eventKey !== a.observation.eventKey ||
|
|
388
|
+
b.observation.consecutiveErrors !== a.observation.consecutiveErrors))
|
|
389
|
+
invalid('attempt reservation');
|
|
390
|
+
}
|
|
391
|
+
else if (b.observation && b.observation.attempts !== 1)
|
|
392
|
+
invalid('initial observation budget');
|
|
309
393
|
if (a.status === 'waiting') {
|
|
394
|
+
if (b.status === 'waiting' && b.observation && !same(a.observation, b.observation)) {
|
|
395
|
+
if (b.observation.attempts > (a.observation?.attempts ?? 0) &&
|
|
396
|
+
b.observation.outcome === 'checking')
|
|
397
|
+
return;
|
|
398
|
+
if (a.observation?.outcome === 'checking' &&
|
|
399
|
+
(b.observation.outcome === 'pending' || b.observation.outcome === 'unknown'))
|
|
400
|
+
return;
|
|
401
|
+
invalid('observation update without reservation');
|
|
402
|
+
}
|
|
403
|
+
if (b.status === 'resolved' && b.resolvedAt >= b.deadlineAt)
|
|
404
|
+
invalid('resolution after deadline');
|
|
310
405
|
if (b.status === 'resolved' ||
|
|
311
406
|
b.status === 'expired' ||
|
|
312
407
|
(b.status === 'cancelled' && !b.priorResolution))
|
|
@@ -314,6 +409,7 @@ export function assertEventWaitTransition(previous, next) {
|
|
|
314
409
|
}
|
|
315
410
|
else if (a.status === 'resolved' &&
|
|
316
411
|
b.status === 'cancelled' &&
|
|
412
|
+
same(a.observation, b.observation) &&
|
|
317
413
|
same(b.priorResolution, { resolvedAt: a.resolvedAt, resolution: a.resolution }))
|
|
318
414
|
return;
|
|
319
415
|
invalid('state transition');
|
|
@@ -59,8 +59,8 @@ export class PluginExecutorBackend {
|
|
|
59
59
|
yield event;
|
|
60
60
|
queue.ackConsumed();
|
|
61
61
|
}
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
62
|
+
const settlement = await producer;
|
|
63
|
+
if (settlement === 'published') {
|
|
64
64
|
// The Runtime Kernel requests the next item only after onSessionEvent
|
|
65
65
|
// resolves. Reaching this point means its terminal event was accepted.
|
|
66
66
|
// The Plugin decides whether its external execution actually settled;
|
|
@@ -78,8 +78,11 @@ export class PluginExecutorBackend {
|
|
|
78
78
|
finally {
|
|
79
79
|
queue.noteConsumerDetached();
|
|
80
80
|
abort.abort(new Error('Plugin executor event consumer detached'));
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
// A settled result that was not acknowledged is abandoned, including one
|
|
82
|
+
// the Runtime rejected before publishing it. A provider failure before
|
|
83
|
+
// settlement is neither acknowledged nor abandoned.
|
|
84
|
+
const settlement = await producer.catch(() => 'unsettled');
|
|
85
|
+
if (settlement !== 'unsettled' &&
|
|
83
86
|
this.#binding.acknowledgeExecution &&
|
|
84
87
|
!acknowledged &&
|
|
85
88
|
this.#binding.abandonExecution)
|
|
@@ -113,6 +116,7 @@ export class PluginExecutorBackend {
|
|
|
113
116
|
let result;
|
|
114
117
|
let failure;
|
|
115
118
|
let failed = false;
|
|
119
|
+
let settled = false;
|
|
116
120
|
try {
|
|
117
121
|
result = await this.#binding.execute({
|
|
118
122
|
sessionId: this.sessionId,
|
|
@@ -130,6 +134,9 @@ export class PluginExecutorBackend {
|
|
|
130
134
|
...(input.quotes ? { quotes: input.quotes } : {}),
|
|
131
135
|
}, {
|
|
132
136
|
signal,
|
|
137
|
+
onSettled: () => {
|
|
138
|
+
settled = true;
|
|
139
|
+
},
|
|
133
140
|
onEvent: (event) => {
|
|
134
141
|
if (event.type === 'thinking_delta')
|
|
135
142
|
thinkingText += event.text;
|
|
@@ -148,14 +155,14 @@ export class PluginExecutorBackend {
|
|
|
148
155
|
this.#publishCancellation(turnId, { status: 'cancelled', source: 'caller' }, queue);
|
|
149
156
|
else
|
|
150
157
|
this.#publishFailure(turnId, failure instanceof Error ? failure.message : 'External executor failed', undefined, false, queue);
|
|
151
|
-
return
|
|
158
|
+
return settled ? 'rejected' : 'unsettled';
|
|
152
159
|
}
|
|
153
160
|
if (result === undefined) {
|
|
154
161
|
this.#publishFailure(turnId, 'External executor returned no terminal result', undefined, false, queue);
|
|
155
|
-
return
|
|
162
|
+
return settled ? 'rejected' : 'unsettled';
|
|
156
163
|
}
|
|
157
164
|
this.#publishResult(turnId, messageId, result, queue);
|
|
158
|
-
return
|
|
165
|
+
return 'published';
|
|
159
166
|
}
|
|
160
167
|
async #requestPermission(input, request, signal, toolUseIds, queue) {
|
|
161
168
|
const hosted = input.hostedInteraction;
|
|
@@ -280,6 +280,12 @@ export class PluginExecutorService extends Service {
|
|
|
280
280
|
return normalizePermissionResult(result, normalized);
|
|
281
281
|
},
|
|
282
282
|
});
|
|
283
|
+
try {
|
|
284
|
+
options.onSettled?.();
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// A settlement observer must not change external execution.
|
|
288
|
+
}
|
|
283
289
|
if (signal.aborted) {
|
|
284
290
|
const cancelled = cancelledResult(signal.reason);
|
|
285
291
|
const normalized = normalizeResult(result);
|
|
@@ -27,25 +27,6 @@ const ANTIGRAVITY_ENTRY_ID = 'antigravity-acp';
|
|
|
27
27
|
const STAGING_DIRECTORY = 'builtin-plugin-staging-v1';
|
|
28
28
|
const RUNTIME_ENTRY = 'plugin.mjs';
|
|
29
29
|
const COMPOSITION_PATCH = 'maka.composition.json';
|
|
30
|
-
// Built-in setup entries remain discoverable before their Plugin is installed.
|
|
31
|
-
// Registered Plugins replace these placeholders with their live catalogs.
|
|
32
|
-
const BUILTIN_EXTERNAL_AGENT_CATALOG = Object.freeze([
|
|
33
|
-
Object.freeze({
|
|
34
|
-
id: ANTIGRAVITY_ENTRY_ID,
|
|
35
|
-
displayName: 'Antigravity',
|
|
36
|
-
readiness: 'unavailable',
|
|
37
|
-
models: Object.freeze([]),
|
|
38
|
-
supportsAttachments: false,
|
|
39
|
-
supportsModelChange: false,
|
|
40
|
-
}),
|
|
41
|
-
]);
|
|
42
|
-
export function withBuiltinExternalAgentCatalog(catalog) {
|
|
43
|
-
const registered = new Set(catalog.map((entry) => entry.id));
|
|
44
|
-
return [
|
|
45
|
-
...catalog,
|
|
46
|
-
...BUILTIN_EXTERNAL_AGENT_CATALOG.filter((entry) => !registered.has(entry.id)),
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
30
|
/**
|
|
50
31
|
* Projects durable product settings into reserved Plugin packages and Entries.
|
|
51
32
|
*
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
* or more contributor license agreements. See the NOTICE file
|
|
4
|
+
* distributed with this work for additional information
|
|
5
|
+
* regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
* to you under the Apache License, Version 2.0 (the
|
|
7
|
+
* "License"); you may not use this file except in compliance
|
|
8
|
+
* with the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing,
|
|
13
|
+
* software distributed under the License is distributed on an
|
|
14
|
+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
* KIND, either express or implied. See the License for the
|
|
16
|
+
* specific language governing permissions and limitations
|
|
17
|
+
* under the License.
|
|
18
|
+
*/
|
|
19
|
+
function key(provider, resource, condition, version) {
|
|
20
|
+
return JSON.stringify([provider, resource, condition, version]);
|
|
21
|
+
}
|
|
22
|
+
export class EventWaitAdapterRegistry {
|
|
23
|
+
#adapters = new Map();
|
|
24
|
+
constructor(adapters) {
|
|
25
|
+
for (const adapter of adapters) {
|
|
26
|
+
if (!['snapshot', 'history'].includes(adapter.reconciliation) ||
|
|
27
|
+
!Number.isSafeInteger(adapter.conditionVersion) ||
|
|
28
|
+
adapter.conditionVersion < 1 ||
|
|
29
|
+
[adapter.providerId, adapter.resourceType, adapter.conditionType].some((v) => typeof v !== 'string' || !/^[A-Za-z0-9_.-]{1,128}$/.test(v)))
|
|
30
|
+
throw new TypeError('Invalid event wait adapter');
|
|
31
|
+
const id = key(adapter.providerId, adapter.resourceType, adapter.conditionType, adapter.conditionVersion);
|
|
32
|
+
if (this.#adapters.has(id))
|
|
33
|
+
throw new TypeError('Duplicate event wait adapter');
|
|
34
|
+
this.#adapters.set(id, adapter);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
get(wait) {
|
|
38
|
+
return this.#adapters.get(key(wait.resource.providerId, wait.resource.resourceType, wait.condition.typeId, wait.condition.version));
|
|
39
|
+
}
|
|
40
|
+
validate(wait) {
|
|
41
|
+
const adapter = this.get(wait);
|
|
42
|
+
if (!adapter)
|
|
43
|
+
throw new TypeError('Unsupported event wait condition');
|
|
44
|
+
adapter.validate(wait);
|
|
45
|
+
return adapter;
|
|
46
|
+
}
|
|
47
|
+
}
|