maka-agent 0.2.1-dev.89.20261016 → 0.2.1-dev.91.20261018
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/core/dist/runtime-event.js +3 -0
- package/node_modules/@maka/core/dist/session.js +2 -1
- package/node_modules/@maka/core/dist/turn-origin.js +14 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +22 -0
- package/node_modules/@maka/runtime/dist/plugin-agent-service.js +7 -1
- 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/dist/runtime-event-backfill.js +1 -0
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +1 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/avatar.js +89 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/bridge.js +52 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/capabilities.js +52 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/controller.js +300 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/conversation.js +116 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/host.js +133 -0
- package/node_modules/@maka/runtime-host/dist/assistant/bot/store.js +223 -0
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/assistant.js +3 -1
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/controller.js +61 -4
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/host.js +88 -14
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/prompt.js +1 -2
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/store.js +4 -3
- package/node_modules/@maka/runtime-host/dist/assistant/memory-network/controller.js +185 -29
- package/node_modules/@maka/runtime-host/dist/assistant/memory-network/host.js +18 -11
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/clarification.js +124 -0
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/controller.js +23 -2
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/delegation.js +29 -11
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/delivery.js +11 -0
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/host.js +2 -7
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/prompt.js +1 -1
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/store.js +5 -3
- package/node_modules/@maka/runtime-host/dist/assistant/service.js +23 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +7 -1
- package/node_modules/@maka/runtime-host/dist/protocol/message.js +20 -1
- package/node_modules/@maka/runtime-host/dist/protocol/personal-assistant.js +33 -0
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +3 -1
- package/node_modules/@maka/runtime-host/dist/server/avatar-image-source.js +66 -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 +156 -36
- package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +3 -2
- package/node_modules/@maka/storage/dist/agent-run-store-contract.js +3 -1
- package/node_modules/@maka/storage/dist/cursor-session-adapter.js +604 -0
- 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/external-session-adapters.js +2 -0
- 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');
|
|
@@ -173,6 +173,7 @@ export function runtimeEventEnvelopeValueDomains() {
|
|
|
173
173
|
}
|
|
174
174
|
const RUNTIME_EVENT_SHAPE = defineObjectShape()(['id', 'invocationId', 'runId', 'sessionId', 'turnId', 'ts', 'partial', 'role', 'author'], ['branch', 'origin', 'modelVisibility', 'status', 'content', 'actions', 'refs']);
|
|
175
175
|
const TEXT_CONTENT_SHAPE = defineObjectShape()(['kind', 'text'], [
|
|
176
|
+
'summary',
|
|
176
177
|
'displayText',
|
|
177
178
|
'interrupted',
|
|
178
179
|
'origin',
|
|
@@ -377,6 +378,7 @@ export function decodeRuntimeEvent(value) {
|
|
|
377
378
|
content: {
|
|
378
379
|
kind: 'text',
|
|
379
380
|
...normalizeMessageContent(value.content),
|
|
381
|
+
...(value.content.summary !== undefined ? { summary: value.content.summary } : {}),
|
|
380
382
|
...(value.content.origin !== undefined
|
|
381
383
|
? { origin: decodeTurnOrigin(value.content.origin) }
|
|
382
384
|
: {}),
|
|
@@ -403,6 +405,7 @@ function isRuntimeEventContent(value) {
|
|
|
403
405
|
switch (value.kind) {
|
|
404
406
|
case 'text':
|
|
405
407
|
if (!hasExactShape(value, TEXT_CONTENT_SHAPE) ||
|
|
408
|
+
(value.summary !== undefined && typeof value.summary !== 'string') ||
|
|
406
409
|
(value.origin !== undefined && !isTurnOrigin(value.origin)) ||
|
|
407
410
|
(value.steering !== undefined && value.steering !== true) ||
|
|
408
411
|
(value.interrupted !== undefined && value.interrupted !== true) ||
|
|
@@ -393,7 +393,7 @@ const USER_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 't
|
|
|
393
393
|
'coordinationActionId',
|
|
394
394
|
'origin',
|
|
395
395
|
]);
|
|
396
|
-
const ASSISTANT_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 'text', 'modelId'], ['thinking', 'contentOrder', 'providerOptions', 'interrupted']);
|
|
396
|
+
const ASSISTANT_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 'text', 'modelId'], ['thinking', 'contentOrder', 'providerOptions', 'interrupted', 'summary']);
|
|
397
397
|
const TOOL_CALL_MESSAGE_SHAPE = defineObjectShape()(['type', 'id', 'turnId', 'ts', 'toolName', 'args'], [
|
|
398
398
|
'activityKind',
|
|
399
399
|
'displayName',
|
|
@@ -610,6 +610,7 @@ function decodeMessage(value, decodeToolResultContent) {
|
|
|
610
610
|
hasMessageEnvelope(message, true) &&
|
|
611
611
|
typeof message.text === 'string' &&
|
|
612
612
|
typeof message.modelId === 'string' &&
|
|
613
|
+
(message.summary === undefined || typeof message.summary === 'string') &&
|
|
613
614
|
(message.interrupted === undefined || message.interrupted === true) &&
|
|
614
615
|
(message.providerOptions === undefined || isRecord(message.providerOptions)) &&
|
|
615
616
|
(message.thinking === undefined || isAssistantThinking(message.thinking)) &&
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { defineObjectShape, hasExactShape, isRecord } from './record-schema.js';
|
|
20
|
+
const EXTERNAL_CONNECTOR_ORIGIN_SHAPE = defineObjectShape()(['kind', 'connectorId'], ['senderId']);
|
|
20
21
|
const SCHEDULED_TASK_ORIGIN_SHAPE = defineObjectShape()(['kind', 'scheduledTaskId'], []);
|
|
21
22
|
const CLOUD_ACTIVATION_ORIGIN_SHAPE = defineObjectShape()(['kind', 'activationId'], []);
|
|
22
23
|
const LEGACY_AUTOMATION_ORIGIN_SHAPE = defineObjectShape()(['kind', 'automationId'], []);
|
|
@@ -26,6 +27,19 @@ const AGENT_GRAPH_ORIGIN_SHAPE = defineObjectShape()(['kind', 'graphId', 'wakeId
|
|
|
26
27
|
export function decodeTurnOrigin(value) {
|
|
27
28
|
if (!isRecord(value))
|
|
28
29
|
return undefined;
|
|
30
|
+
if (value.kind === 'external_connector') {
|
|
31
|
+
if (!hasExactShape(value, EXTERNAL_CONNECTOR_ORIGIN_SHAPE) ||
|
|
32
|
+
typeof value.connectorId !== 'string' ||
|
|
33
|
+
!value.connectorId.length ||
|
|
34
|
+
(value.senderId !== undefined &&
|
|
35
|
+
(typeof value.senderId !== 'string' || !value.senderId.length)))
|
|
36
|
+
return undefined;
|
|
37
|
+
return {
|
|
38
|
+
kind: 'external_connector',
|
|
39
|
+
connectorId: value.connectorId,
|
|
40
|
+
...(value.senderId !== undefined ? { senderId: value.senderId } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
29
43
|
if (value.kind === 'workhub_result') {
|
|
30
44
|
const keys = ['kind', 'eventId', 'actionId', 'delegationId', 'targetSessionId', 'targetTurnId'];
|
|
31
45
|
if (Object.keys(value).length !== keys.length ||
|
|
@@ -1748,6 +1748,7 @@ export class AiSdkTurn {
|
|
|
1748
1748
|
throw providerOutcome.failure;
|
|
1749
1749
|
finishReason = providerOutcome.finishReason;
|
|
1750
1750
|
await queue.waitUntilConsumedThroughCurrent();
|
|
1751
|
+
let toolRequestedFinish = false;
|
|
1751
1752
|
if (returnedToolCalls.length > 0) {
|
|
1752
1753
|
const continuationBudgetRemains = maxSteps === undefined || runtimeSteps < maxSteps;
|
|
1753
1754
|
if (continuationBudgetRemains && !this.deps.backend.loadTurnRuntimeEvents) {
|
|
@@ -1819,6 +1820,12 @@ export class AiSdkTurn {
|
|
|
1819
1820
|
throw outcome.reason;
|
|
1820
1821
|
const settlement = outcome.value;
|
|
1821
1822
|
const toolCall = returnedToolCalls[index];
|
|
1823
|
+
const definition = toolCall && toolsByName.get(toolCall.toolName);
|
|
1824
|
+
if (returnedToolCalls.length === 1 &&
|
|
1825
|
+
definition?.executionSemantics === 'exclusive_step' &&
|
|
1826
|
+
definition.finishTurnOnSuccess &&
|
|
1827
|
+
!settlement.providerError)
|
|
1828
|
+
toolRequestedFinish = true;
|
|
1822
1829
|
if (isPlanToolResult(settlement.result)) {
|
|
1823
1830
|
this.handlePlanToolResult(settlement.result, queue);
|
|
1824
1831
|
}
|
|
@@ -1851,6 +1858,21 @@ export class AiSdkTurn {
|
|
|
1851
1858
|
...(providerStepUsage ? { usage: providerStepUsage } : {}),
|
|
1852
1859
|
});
|
|
1853
1860
|
lastCompletedStepHadToolResult = returnedToolCalls.length > 0;
|
|
1861
|
+
// Finish only after the tool result is durably consumed. The Host can
|
|
1862
|
+
// veto completion (for example, new Inbox input raced with Sleep).
|
|
1863
|
+
if (toolRequestedFinish && !this.aborted && !this.loopStopRequested) {
|
|
1864
|
+
const decision = await this.deps.backend.beforeTurnFinish?.({
|
|
1865
|
+
sessionId: this.deps.backend.sessionId,
|
|
1866
|
+
turnId,
|
|
1867
|
+
signal: turnAbortController.signal,
|
|
1868
|
+
});
|
|
1869
|
+
if (!decision || decision.allow) {
|
|
1870
|
+
finishReason = 'stop';
|
|
1871
|
+
this.deps.modelAdapter.clearContinuation(this.turnId);
|
|
1872
|
+
break agentLoop;
|
|
1873
|
+
}
|
|
1874
|
+
finishFeedback = decision.feedback;
|
|
1875
|
+
}
|
|
1854
1876
|
const stepLimitReached = maxSteps !== undefined && runtimeSteps >= maxSteps;
|
|
1855
1877
|
if (sandboxBoundaryFinalizationStep ||
|
|
1856
1878
|
(stepLimitReached &&
|
|
@@ -100,7 +100,13 @@ export class PluginAgentService extends Service {
|
|
|
100
100
|
return Object.freeze({
|
|
101
101
|
...descriptor,
|
|
102
102
|
id,
|
|
103
|
-
followup: (message) => service.runtime().followup(id, message, invocation),
|
|
103
|
+
followup: (message, options) => service.runtime().followup(id, message, invocation, options),
|
|
104
|
+
messageExecution: async (messageId) => {
|
|
105
|
+
const runtime = service.runtime();
|
|
106
|
+
if (!runtime.messageExecution)
|
|
107
|
+
throw new Error('Runtime message execution query unavailable');
|
|
108
|
+
return runtime.messageExecution(id, messageId, invocation);
|
|
109
|
+
},
|
|
104
110
|
steer: (message) => service.runtime().steer(id, message, invocation),
|
|
105
111
|
inject: (message) => service.runtime().inject(id, message, invocation),
|
|
106
112
|
cancel: () => service.runtime().cancel(id, invocation),
|
|
@@ -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);
|
|
@@ -90,6 +90,7 @@ export function backfillRuntimeEventsFromStoredMessages(input) {
|
|
|
90
90
|
content: {
|
|
91
91
|
kind: 'text',
|
|
92
92
|
text: message.text,
|
|
93
|
+
...(message.summary !== undefined ? { summary: message.summary } : {}),
|
|
93
94
|
...(message.interrupted ? { interrupted: true } : {}),
|
|
94
95
|
},
|
|
95
96
|
actions: { stateDelta: recoveryState(now, message) },
|
|
@@ -659,6 +659,7 @@ function projectText(event, state, messages) {
|
|
|
659
659
|
turnId: event.turnId,
|
|
660
660
|
ts: event.ts,
|
|
661
661
|
text: event.content.text,
|
|
662
|
+
...(event.content.summary !== undefined ? { summary: event.content.summary } : {}),
|
|
662
663
|
...(event.content.interrupted ? { interrupted: true } : {}),
|
|
663
664
|
...(event.content.providerOptions !== undefined
|
|
664
665
|
? { providerOptions: structuredClone(event.content.providerOptions) }
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
import { createHash } from 'node:crypto';
|
|
20
|
+
import { z } from 'zod';
|
|
21
|
+
import { validateImageBytes } from '@maka/runtime/image-file';
|
|
22
|
+
import { assistantAvatarSchema, assistantEmojiSchema, DEFAULT_ASSISTANT_AVATAR, } from '../../protocol/personal-assistant.js';
|
|
23
|
+
export function initializeAvatar(ctx, store, bot, register) {
|
|
24
|
+
const current = () => store.get('avatar');
|
|
25
|
+
const avatar = () => {
|
|
26
|
+
const { imageData: _, ...metadata } = current() ?? {};
|
|
27
|
+
return assistantAvatarSchema.safeParse(metadata).data ?? DEFAULT_ASSISTANT_AVATAR;
|
|
28
|
+
};
|
|
29
|
+
ctx.api.register('assistant.avatar', () => ({ avatar: avatar() }));
|
|
30
|
+
// Bounded transfers keep large images out of the normal status polling and model transcript.
|
|
31
|
+
ctx.api.register('assistant.avatar-image', (raw) => {
|
|
32
|
+
const { id, offset } = z
|
|
33
|
+
.object({ id: z.string(), offset: z.number().int().nonnegative().default(0) })
|
|
34
|
+
.parse(raw);
|
|
35
|
+
const saved = current();
|
|
36
|
+
if (avatar().image?.id !== id || !saved?.imageData)
|
|
37
|
+
throw Error('Avatar changed; refresh its metadata');
|
|
38
|
+
if (offset >= saved.imageData.length)
|
|
39
|
+
throw Error('Invalid avatar offset');
|
|
40
|
+
const data = saved.imageData.slice(offset, offset + 192 * 1024);
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
offset,
|
|
44
|
+
data,
|
|
45
|
+
nextOffset: offset + data.length < saved.imageData.length ? offset + data.length : null,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
register('AssistantSetAvatar', '更换你自己的头像,入口和聊天消息同步显示,重启后保留。image 填生成或找到的图片的当前工具会话工作目录内的本地路径或公网 HTTP(S) 图片网址(PNG/JPEG/WebP/GIF,最多 2 MiB),不要传 base64。也可用 emoji 填一个 emoji;两者选一。background 可填 #RRGGBB。图片会保存副本;此工具只设置头像,生成或找图使用已有工具,必要时委派任务。不会发送消息。', z
|
|
49
|
+
.object({
|
|
50
|
+
emoji: assistantEmojiSchema.optional(),
|
|
51
|
+
image: z.string().trim().min(1).max(8192).optional(),
|
|
52
|
+
background: z
|
|
53
|
+
.string()
|
|
54
|
+
.regex(/^#[0-9a-fA-F]{6}$/)
|
|
55
|
+
.default('#E5EDFF'),
|
|
56
|
+
})
|
|
57
|
+
.strict()
|
|
58
|
+
.refine((value) => Boolean(value.emoji) !== Boolean(value.image), 'Provide exactly one of emoji or image'), async (input, call) => {
|
|
59
|
+
let saved;
|
|
60
|
+
if (input.image) {
|
|
61
|
+
if (!ctx.readAvatarImage)
|
|
62
|
+
throw Error('Image loading is unavailable on this Host');
|
|
63
|
+
const worker = bot.worker();
|
|
64
|
+
const owner = bot.owner();
|
|
65
|
+
const result = await ctx.readAvatarImage(call.sessionId, input.image, call.abortSignal);
|
|
66
|
+
call.abortSignal.throwIfAborted();
|
|
67
|
+
bot.ready();
|
|
68
|
+
if (bot.worker() !== worker || bot.owner() !== owner)
|
|
69
|
+
throw Error('Assistant binding changed');
|
|
70
|
+
const image = validateImageBytes(result.bytes, 'chat');
|
|
71
|
+
const bytes = Buffer.from(image.bytes);
|
|
72
|
+
const imageData = bytes.toString('base64');
|
|
73
|
+
saved = {
|
|
74
|
+
background: input.background,
|
|
75
|
+
image: {
|
|
76
|
+
id: createHash('sha256').update(bytes).digest('hex'),
|
|
77
|
+
mimeType: image.mimeType,
|
|
78
|
+
length: imageData.length,
|
|
79
|
+
},
|
|
80
|
+
imageData,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
saved = { emoji: input.emoji, background: input.background };
|
|
85
|
+
// One SQLite value makes image content and metadata an atomic replacement.
|
|
86
|
+
store.set('avatar', saved);
|
|
87
|
+
return { updated: true, avatar: avatar() };
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
/** In-process routing only; durable mailbox and wake state belong to the Bot. */
|
|
20
|
+
export class AssistantBotBridge {
|
|
21
|
+
endpoint;
|
|
22
|
+
register(endpoint) {
|
|
23
|
+
this.endpoint = endpoint;
|
|
24
|
+
}
|
|
25
|
+
status() {
|
|
26
|
+
return this.endpoint?.status() ?? null;
|
|
27
|
+
}
|
|
28
|
+
isConversation(id) {
|
|
29
|
+
return this.endpoint?.owner() === id;
|
|
30
|
+
}
|
|
31
|
+
hasUserMessage(sessionId, id) {
|
|
32
|
+
return this.isConversation(sessionId) && this.endpoint.hasUserMessage(id);
|
|
33
|
+
}
|
|
34
|
+
isWorker(id) {
|
|
35
|
+
return this.endpoint?.worker() === id;
|
|
36
|
+
}
|
|
37
|
+
ownerFor(id) {
|
|
38
|
+
return this.isWorker(id) ? this.endpoint?.owner() : undefined;
|
|
39
|
+
}
|
|
40
|
+
async receive(sessionId, id, content, origin) {
|
|
41
|
+
if (!this.isConversation(sessionId))
|
|
42
|
+
return false;
|
|
43
|
+
await this.endpoint.receive(id, content, origin);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
post(owner, input) {
|
|
47
|
+
if (!this.isConversation(owner))
|
|
48
|
+
return false;
|
|
49
|
+
this.endpoint.post(input);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
export const BOT_TOOLS = new Set([
|
|
20
|
+
'InboxList',
|
|
21
|
+
'InboxRead',
|
|
22
|
+
'InboxCheck',
|
|
23
|
+
'SendUser',
|
|
24
|
+
'AssistantSetAvatar',
|
|
25
|
+
'Sleep',
|
|
26
|
+
'AssistantConversation',
|
|
27
|
+
'MatterDelegate',
|
|
28
|
+
'MatterTasks',
|
|
29
|
+
'MatterTaskMessage',
|
|
30
|
+
'MatterReply',
|
|
31
|
+
'MatterTaskControl',
|
|
32
|
+
'MatterDelegationRecover',
|
|
33
|
+
'MatterFeedbackRetry',
|
|
34
|
+
'InitiativeEnable',
|
|
35
|
+
'InitiativeConfigure',
|
|
36
|
+
'InitiativeControl',
|
|
37
|
+
'InitiativeStatus',
|
|
38
|
+
'MemoryIndexCreate',
|
|
39
|
+
'MemoryIndexCreateGroup',
|
|
40
|
+
'MemoryIndexMaintain',
|
|
41
|
+
'MemoryIndexControl',
|
|
42
|
+
'MemoryIndexAddSources',
|
|
43
|
+
'MemoryIndexList',
|
|
44
|
+
'MemoryIndexRead',
|
|
45
|
+
'MemoryIndexContent',
|
|
46
|
+
'MemoryIndexLinks',
|
|
47
|
+
'MemoryOriginal',
|
|
48
|
+
'MemoryHistory',
|
|
49
|
+
'MemorySources',
|
|
50
|
+
'MemoryRange',
|
|
51
|
+
]);
|
|
52
|
+
export const BOT_DELEGATION_TOOLS = new Set([...BOT_TOOLS].filter((name) => name.startsWith('Matter') || name.startsWith('Initiative') || name.startsWith('Memory')));
|