maka-agent 0.2.2-dev.101.20261026 → 0.2.2-dev.106.20261030
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/THIRD_PARTY_NOTICES.txt +2 -2
- package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/runtime-policy/connection-catalog-codec.js +6 -0
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +21 -7
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +32 -4
- package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +13 -4
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/assistant.js +3 -0
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/controller.js +27 -2
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/notebook.js +73 -7
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/prompt.js +2 -2
- package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/quick.js +16 -0
- package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/controller.js +26 -0
- package/node_modules/@maka/runtime-host/dist/assistant/run-cost.js +189 -0
- package/node_modules/@maka/runtime-host/dist/assistant/service.js +4 -0
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/server/canonical-usage-reader.js +24 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +2 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -0
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +2 -0
- package/node_modules/@maka/runtime-host/package.json +1 -1
- package/node_modules/qs/.editorconfig +1 -1
- package/node_modules/qs/.github/THREAT_MODEL.md +4 -2
- package/node_modules/qs/CHANGELOG.md +61 -14
- package/node_modules/qs/README.md +57 -4
- package/node_modules/qs/dist/qs.js +19 -19
- package/node_modules/qs/eslint.config.mjs +61 -50
- package/node_modules/qs/lib/formats.js +7 -2
- package/node_modules/qs/lib/parse.js +105 -8
- package/node_modules/qs/lib/stringify.js +9 -7
- package/node_modules/qs/lib/utils.js +145 -13
- package/node_modules/qs/package.json +8 -8
- package/node_modules/systeminformation/lib/filesystem.js +2 -1
- package/node_modules/systeminformation/lib/graphics.js +1 -1
- package/node_modules/systeminformation/lib/index.js +10 -8
- package/node_modules/systeminformation/lib/network.js +29 -20
- package/node_modules/systeminformation/lib/processes.js +67 -79
- package/node_modules/systeminformation/lib/users.js +1 -1
- package/node_modules/systeminformation/lib/util.js +75 -30
- package/node_modules/systeminformation/package.json +1 -1
- package/package.json +1 -1
package/THIRD_PARTY_NOTICES.txt
CHANGED
|
@@ -6673,7 +6673,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
6673
6673
|
|
|
6674
6674
|
================================================================================
|
|
6675
6675
|
|
|
6676
|
-
Package: qs@6.
|
|
6676
|
+
Package: qs@6.17.0
|
|
6677
6677
|
Declared license: BSD-3-Clause
|
|
6678
6678
|
Selected license: BSD-3-Clause
|
|
6679
6679
|
Repository: https://github.com/ljharb/qs.git
|
|
@@ -7361,7 +7361,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
|
7361
7361
|
|
|
7362
7362
|
================================================================================
|
|
7363
7363
|
|
|
7364
|
-
Package: systeminformation@5.33.
|
|
7364
|
+
Package: systeminformation@5.33.18
|
|
7365
7365
|
Declared license: MIT
|
|
7366
7366
|
Selected license: MIT
|
|
7367
7367
|
Repository: git+https://github.com/sebhildebrandt/systeminformation.git
|
|
Binary file
|
|
Binary file
|
|
@@ -446,6 +446,7 @@ export function decodeConnectionModel(value) {
|
|
|
446
446
|
'description',
|
|
447
447
|
'apiProtocol',
|
|
448
448
|
'contextWindow',
|
|
449
|
+
'codexCompactionHash',
|
|
449
450
|
'inputLimit',
|
|
450
451
|
'maxOutputTokens',
|
|
451
452
|
'knowledgeCutoff',
|
|
@@ -492,6 +493,11 @@ export function decodeConnectionModel(value) {
|
|
|
492
493
|
: {
|
|
493
494
|
contextWindow: integerValue(item.contextWindow, 'model context window', 1, Number.MAX_SAFE_INTEGER),
|
|
494
495
|
}),
|
|
496
|
+
...(item.codexCompactionHash === undefined
|
|
497
|
+
? {}
|
|
498
|
+
: {
|
|
499
|
+
codexCompactionHash: nonEmptyStringValue(item.codexCompactionHash, 'Codex compaction hash', 128),
|
|
500
|
+
}),
|
|
495
501
|
...(item.inputLimit === undefined
|
|
496
502
|
? {}
|
|
497
503
|
: {
|
|
@@ -230,27 +230,41 @@ function mergeTextProviderOptions(current, next, textOffset) {
|
|
|
230
230
|
function projectToolModePlan(plan, toolMode, execTool) {
|
|
231
231
|
if (toolMode === 'direct')
|
|
232
232
|
return plan;
|
|
233
|
+
const directTools = plan.providerTools.filter((tool) => tool.nesting === 'direct_only');
|
|
234
|
+
const directNames = new Set(directTools.map((tool) => tool.name));
|
|
235
|
+
const projectActive = (names) => [
|
|
236
|
+
execTool.name,
|
|
237
|
+
...names.filter((name) => directNames.has(name)),
|
|
238
|
+
];
|
|
239
|
+
const projectBaseActive = plan.projectActiveTools;
|
|
233
240
|
return {
|
|
234
241
|
...plan,
|
|
235
242
|
providerTools: [
|
|
236
243
|
execTool,
|
|
244
|
+
...directTools,
|
|
237
245
|
...plan.providerTools.filter((tool) => tool.name === INVALID_TOOL_NAME),
|
|
238
246
|
],
|
|
239
|
-
activeTools:
|
|
240
|
-
...(
|
|
241
|
-
? {
|
|
247
|
+
activeTools: projectActive(plan.activeTools),
|
|
248
|
+
...(projectBaseActive
|
|
249
|
+
? {
|
|
250
|
+
projectActiveTools: (options) => ({
|
|
251
|
+
activeTools: projectActive(projectBaseActive(options).activeTools),
|
|
252
|
+
}),
|
|
253
|
+
}
|
|
242
254
|
: {}),
|
|
243
|
-
currentRepairToolNames: () =>
|
|
255
|
+
currentRepairToolNames: () => projectActive(plan.currentRepairToolNames()),
|
|
244
256
|
diagnostics: () => undefined,
|
|
245
257
|
};
|
|
246
258
|
}
|
|
247
|
-
function renderCodeModeCatalogPrompt(nested) {
|
|
259
|
+
function renderCodeModeCatalogPrompt(nested, directNames) {
|
|
248
260
|
// The aggregate catalog can exceed the evidence codec's bound for one tool
|
|
249
261
|
// description. Keep exec's schema fixed; requestSystemPrompt and its hash
|
|
250
262
|
// carry this step's exact, refreshable nested surface instead.
|
|
251
263
|
const catalog = requestCompositionToolSchemas([...nested.values()], [...nested.keys()]);
|
|
252
264
|
return [
|
|
253
|
-
|
|
265
|
+
directNames.length
|
|
266
|
+
? `Code Mode: call the tools in the following catalog from inside exec. Call these control tools directly, never from exec: ${directNames.join(', ')}.`
|
|
267
|
+
: 'Code Mode: exec is the only callable tool. Call the following tools from inside exec.',
|
|
254
268
|
'After tool_search, return its result and use the refreshed catalog in the next exec call.',
|
|
255
269
|
JSON.stringify(catalog),
|
|
256
270
|
].join('\n');
|
|
@@ -1196,7 +1210,7 @@ export class AiSdkTurn {
|
|
|
1196
1210
|
const codeModeCatalogPrompt = toolMode === 'code_mode'
|
|
1197
1211
|
? renderCodeModeCatalogPrompt(toolRuntime.hasSandboxBoundaryDenial()
|
|
1198
1212
|
? new Map([...nestedTools].filter(([name]) => name !== REQUEST_SANDBOX_BOUNDARY_TOOL_NAME))
|
|
1199
|
-
: nestedTools)
|
|
1213
|
+
: nestedTools, boundaryAwareToolNames(plan.currentRepairToolNames()).filter((name) => name !== codeModeExecTool.name))
|
|
1200
1214
|
: undefined;
|
|
1201
1215
|
const resolveDispatch = (active) => {
|
|
1202
1216
|
const activeTools = finalChildSummaryStep || sandboxBoundaryFinalizationStep
|
|
@@ -204,13 +204,36 @@ export function isProviderHistoryCompactCheckpoint(checkpoint) {
|
|
|
204
204
|
export function isTextHistoryCompactCheckpoint(checkpoint) {
|
|
205
205
|
return checkpoint.version === 2;
|
|
206
206
|
}
|
|
207
|
+
/** Bound the age of provider compatibility claims, including long-lived backends. */
|
|
208
|
+
export const CODEX_COMPACTION_METADATA_MAX_AGE_MS = 5 * 60 * 1000;
|
|
209
|
+
export function freshCodexCompactionHash(connection, modelId, now = Date.now()) {
|
|
210
|
+
const fetchedAt = connection.codexCompactionMetadataFetchedAt;
|
|
211
|
+
if (connection.providerType !== 'openai-codex' ||
|
|
212
|
+
fetchedAt === undefined ||
|
|
213
|
+
!Number.isSafeInteger(fetchedAt) ||
|
|
214
|
+
fetchedAt < 0 ||
|
|
215
|
+
!Number.isFinite(now) ||
|
|
216
|
+
fetchedAt > now ||
|
|
217
|
+
now - fetchedAt >= CODEX_COMPACTION_METADATA_MAX_AGE_MS)
|
|
218
|
+
return undefined;
|
|
219
|
+
return connection.models?.find((model) => model.id === modelId)?.codexCompactionHash;
|
|
220
|
+
}
|
|
207
221
|
export function canReplayHistoryCompactCheckpointForModel(checkpoint, connection, connectionId, modelId) {
|
|
208
222
|
if (!isProviderHistoryCompactCheckpoint(checkpoint))
|
|
209
223
|
return true;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
224
|
+
const state = checkpoint.providerState;
|
|
225
|
+
if (connection.providerType !== 'openai-codex' ||
|
|
226
|
+
connectionId === undefined ||
|
|
227
|
+
state.connectionId !== connectionId)
|
|
228
|
+
return false;
|
|
229
|
+
const targetHash = freshCodexCompactionHash(connection, modelId);
|
|
230
|
+
// Legacy checkpoints remain usable on their original model. Never infer a
|
|
231
|
+
// legacy checkpoint's original hash from today's mutable model catalog.
|
|
232
|
+
if (state.modelId === modelId && (!state.codexCompactionHash || !targetHash))
|
|
233
|
+
return true;
|
|
234
|
+
return (nonEmpty(state.codexCompactionHash) &&
|
|
235
|
+
nonEmpty(targetHash) &&
|
|
236
|
+
state.codexCompactionHash === targetHash);
|
|
214
237
|
}
|
|
215
238
|
/** Whether this model's configured compactor can roll forward from this checkpoint value. */
|
|
216
239
|
export function canContinueHistoryCompactCheckpointForModel(checkpoint, connection, connectionId, modelId) {
|
|
@@ -582,6 +605,11 @@ function validHistoryCompactProviderState(value) {
|
|
|
582
605
|
return (state.kind === 'openai_codex_remote_v2' &&
|
|
583
606
|
nonEmpty(state.connectionId) &&
|
|
584
607
|
nonEmpty(state.modelId) &&
|
|
608
|
+
(state.codexCompactionHash === undefined ||
|
|
609
|
+
(nonEmpty(state.codexCompactionHash) &&
|
|
610
|
+
state.codexCompactionHash.length <= 128 &&
|
|
611
|
+
state.codexCompactionHash.trim() === state.codexCompactionHash &&
|
|
612
|
+
!/[\u0000-\u001f\u007f]/.test(state.codexCompactionHash))) &&
|
|
585
613
|
nonEmpty(state.itemId) &&
|
|
586
614
|
nonEmpty(state.encryptedContent));
|
|
587
615
|
}
|
|
@@ -300,6 +300,13 @@ async function fetchOpenAiCodexModels(baseUrl, accessToken, fetchFn) {
|
|
|
300
300
|
const contextWindow = providerTokenLimit(model.context_window);
|
|
301
301
|
if (contextWindow !== undefined)
|
|
302
302
|
entry.contextWindow = contextWindow;
|
|
303
|
+
if (typeof model.comp_hash === 'string' &&
|
|
304
|
+
model.comp_hash.length > 0 &&
|
|
305
|
+
model.comp_hash.length <= 128 &&
|
|
306
|
+
model.comp_hash.trim() === model.comp_hash &&
|
|
307
|
+
!/[\u0000-\u001f\u007f]/.test(model.comp_hash)) {
|
|
308
|
+
entry.codexCompactionHash = model.comp_hash;
|
|
309
|
+
}
|
|
303
310
|
return entry;
|
|
304
311
|
});
|
|
305
312
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { HistoryCompactSummarizerError } from './history-compact-error.js';
|
|
20
20
|
import { nonEmpty } from './context-budget-helpers.js';
|
|
21
|
-
import { canContinueHistoryCompactCheckpointForModel, historyCompactCheckpointToModelMessage, isProviderHistoryCompactCheckpoint, projectHistoryCompactCheckpointReplay, } from './history-compact-checkpoint.js';
|
|
21
|
+
import { canContinueHistoryCompactCheckpointForModel, freshCodexCompactionHash, historyCompactCheckpointToModelMessage, isProviderHistoryCompactCheckpoint, projectHistoryCompactCheckpointReplay, } from './history-compact-checkpoint.js';
|
|
22
22
|
import { admitProviderReasoningReplayItems, buildRuntimeEventModelReplayPlan, buildRuntimeEventReplayTimeline, compatibleProviderReasoningReplayEventIds, } from './model-history.js';
|
|
23
23
|
import { withProviderStreamTracking } from './provider-request-telemetry.js';
|
|
24
24
|
import { effectiveReplayToolResultOutput } from './durable-tool-result-projection.js';
|
|
@@ -40,7 +40,11 @@ export function buildOpenAiCodexHistoryCompactor(options) {
|
|
|
40
40
|
try {
|
|
41
41
|
const previous = input.previousCheckpoint;
|
|
42
42
|
const canContinuePrevious = previous &&
|
|
43
|
-
canContinueHistoryCompactCheckpointForModel(previous, {
|
|
43
|
+
canContinueHistoryCompactCheckpointForModel(previous, {
|
|
44
|
+
providerType: 'openai-codex',
|
|
45
|
+
models: options.models,
|
|
46
|
+
codexCompactionMetadataFetchedAt: options.codexCompactionMetadataFetchedAt,
|
|
47
|
+
}, options.connectionId, options.modelId);
|
|
44
48
|
const events = canContinuePrevious
|
|
45
49
|
? (input.newlyFoldedRuntimeEvents ?? [])
|
|
46
50
|
: input.source.foldedRuntimeEvents;
|
|
@@ -92,7 +96,11 @@ export function buildOpenAiCodexHistoryCompactor(options) {
|
|
|
92
96
|
}
|
|
93
97
|
if (streamError)
|
|
94
98
|
throw streamError;
|
|
95
|
-
const state = extractOpenAiCodexCompactionState(content, options.connectionId, options.modelId
|
|
99
|
+
const state = extractOpenAiCodexCompactionState(content, options.connectionId, options.modelId, freshCodexCompactionHash({
|
|
100
|
+
providerType: 'openai-codex',
|
|
101
|
+
models: options.models,
|
|
102
|
+
codexCompactionMetadataFetchedAt: options.codexCompactionMetadataFetchedAt,
|
|
103
|
+
}, options.modelId));
|
|
96
104
|
if (!state)
|
|
97
105
|
throw new HistoryCompactSummarizerError('invalid_provider_state');
|
|
98
106
|
return state;
|
|
@@ -256,7 +264,7 @@ function openAiCodexCompactionMessages(events, providerReasoningReplayEventIds)
|
|
|
256
264
|
}
|
|
257
265
|
return messages;
|
|
258
266
|
}
|
|
259
|
-
export function extractOpenAiCodexCompactionState(content, connectionId, modelId) {
|
|
267
|
+
export function extractOpenAiCodexCompactionState(content, connectionId, modelId, codexCompactionHash) {
|
|
260
268
|
const parts = (content ?? []).filter(isOpenAiCompactionPart);
|
|
261
269
|
if (parts.length !== 1)
|
|
262
270
|
return undefined;
|
|
@@ -267,6 +275,7 @@ export function extractOpenAiCodexCompactionState(content, connectionId, modelId
|
|
|
267
275
|
kind: 'openai_codex_remote_v2',
|
|
268
276
|
connectionId,
|
|
269
277
|
modelId,
|
|
278
|
+
...(codexCompactionHash ? { codexCompactionHash } : {}),
|
|
270
279
|
itemId: metadata.itemId,
|
|
271
280
|
encryptedContent: metadata.encryptedContent,
|
|
272
281
|
};
|
|
@@ -92,6 +92,9 @@ export function registerAssistant(ctx, store, controller) {
|
|
|
92
92
|
},
|
|
93
93
|
...(includeMemory ? { memory: memoryState } : {}),
|
|
94
94
|
tasks: taskState,
|
|
95
|
+
// What background discovery and Matter activations cost and whether they reported
|
|
96
|
+
// (#6309). Observability only: nothing in the scheduler reads it.
|
|
97
|
+
cost: ctx.runCost?.summary() ?? null,
|
|
95
98
|
hostRequired: true,
|
|
96
99
|
};
|
|
97
100
|
};
|
|
@@ -315,9 +315,23 @@ export class InitiativeController {
|
|
|
315
315
|
}
|
|
316
316
|
async run(agent, state) {
|
|
317
317
|
const id = state.active.id;
|
|
318
|
+
const startedAt = state.active.startedAt ?? Date.now();
|
|
319
|
+
// Each Session the heartbeat drives and the window it was driven in, so the
|
|
320
|
+
// run's cost can be read back from the Usage ledger afterwards (#6309).
|
|
321
|
+
const segments = [];
|
|
322
|
+
const measured = async (worker, work) => {
|
|
323
|
+
const from = Date.now();
|
|
324
|
+
try {
|
|
325
|
+
await work();
|
|
326
|
+
}
|
|
327
|
+
finally {
|
|
328
|
+
segments.push({ sessionId: worker.sessionId, from, to: Date.now() });
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
let outcome = 'failed';
|
|
318
332
|
this.activeAgent = agent;
|
|
319
333
|
try {
|
|
320
|
-
await this.execute(agent, state);
|
|
334
|
+
await measured(agent, () => this.execute(agent, state));
|
|
321
335
|
if (state.active.phase === 'discovery') {
|
|
322
336
|
const next = this.store.update((s) => {
|
|
323
337
|
if (s.active?.id !== id)
|
|
@@ -330,8 +344,10 @@ export class InitiativeController {
|
|
|
330
344
|
const snapshot = await judge.snapshot();
|
|
331
345
|
if (snapshot?.agent?.status === 'running' || awaitingUser(snapshot))
|
|
332
346
|
throw Error('Judgement worker is busy or awaiting user input; notebook retained');
|
|
333
|
-
await this.execute(judge, this.store.get());
|
|
347
|
+
await measured(judge, () => this.execute(judge, this.store.get()));
|
|
334
348
|
}
|
|
349
|
+
// The signal the run's `lastRun` outcome is derived from: the Bot accepted a report.
|
|
350
|
+
outcome = this.store.get()?.active?.reported ? 'reported' : 'no_report';
|
|
335
351
|
this.store.finish(id);
|
|
336
352
|
}
|
|
337
353
|
catch (e) {
|
|
@@ -352,6 +368,15 @@ export class InitiativeController {
|
|
|
352
368
|
}
|
|
353
369
|
finally {
|
|
354
370
|
this.activeAgent = undefined;
|
|
371
|
+
// Observation only: the ledger swallows its own failures and nothing reads it back.
|
|
372
|
+
await this.ctx.runCost?.record({
|
|
373
|
+
kind: 'discovery',
|
|
374
|
+
runId: id,
|
|
375
|
+
startedAt,
|
|
376
|
+
endedAt: Date.now(),
|
|
377
|
+
outcome,
|
|
378
|
+
segments,
|
|
379
|
+
});
|
|
355
380
|
}
|
|
356
381
|
}
|
|
357
382
|
async enable(input, call) {
|
|
@@ -110,21 +110,44 @@ export class Notebook {
|
|
|
110
110
|
requireThat(topic, 'Unknown topic');
|
|
111
111
|
let current = { status: 'open', reason: null, feedbackId: null };
|
|
112
112
|
for (const e of this.feedbackResolutions().sort((a, b) => this.event(a.payload.feedbackId).seq - this.event(b.payload.feedbackId).seq))
|
|
113
|
-
for (const rule of e.payload.rules) {
|
|
113
|
+
for (const [ruleIndex, rule] of e.payload.rules.entries()) {
|
|
114
114
|
if (!rule.topicIds.includes(topicId) && !overlaps(rule.tags, topic.tags))
|
|
115
115
|
continue;
|
|
116
|
-
current = { ...rule, feedbackId: e.payload.feedbackId };
|
|
116
|
+
current = { ...rule, feedbackId: e.payload.feedbackId, ruleIndex };
|
|
117
117
|
}
|
|
118
|
+
if (current.feedbackId && ['closed', 'deferred'].includes(current.status)) {
|
|
119
|
+
const governing = this.event(current.feedbackId).seq;
|
|
120
|
+
// A reopening answers one specific rule. Topics gain tags over time, so the
|
|
121
|
+
// rule that governs a topic can change with no new feedback event: a later
|
|
122
|
+
// rule may start to match. Apply only a reopening the model judged against
|
|
123
|
+
// the rule that governs now; a different rule was never judged.
|
|
124
|
+
const reopen = this.events('reopen')
|
|
125
|
+
.filter((e) => e.payload.topicId === topicId &&
|
|
126
|
+
e.seq > governing &&
|
|
127
|
+
e.payload.feedbackId === current.feedbackId &&
|
|
128
|
+
e.payload.ruleIndex === current.ruleIndex)
|
|
129
|
+
.at(-1);
|
|
130
|
+
if (reopen)
|
|
131
|
+
current = {
|
|
132
|
+
status: 'open',
|
|
133
|
+
reason: reopen.payload.reason,
|
|
134
|
+
feedbackId: current.feedbackId,
|
|
135
|
+
ruleIndex: current.ruleIndex,
|
|
136
|
+
reopenedByChange: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
118
139
|
if (current.status === 'deferred' &&
|
|
119
140
|
current.resumeAt &&
|
|
120
141
|
Date.parse(now) >= Date.parse(current.resumeAt))
|
|
121
142
|
current = { ...current, status: 'open', resumedByUserDeadline: true };
|
|
122
143
|
const receipts = this.events('delivery').filter((e) => e.payload.topicIds.includes(topicId));
|
|
144
|
+
const withheld = this.events('withheld').filter((e) => e.payload.topicId === topicId);
|
|
123
145
|
// Display 'sent' without destroying the user's open/deferred/closed policy.
|
|
124
146
|
return {
|
|
125
147
|
...current,
|
|
126
148
|
displayStatus: current.status === 'open' && receipts.length ? 'sent' : current.status,
|
|
127
149
|
receipts,
|
|
150
|
+
withheld,
|
|
128
151
|
};
|
|
129
152
|
}
|
|
130
153
|
recordFeedback({ id, text, at = this.clock(), replyTo = null, }) {
|
|
@@ -168,6 +191,42 @@ export class Notebook {
|
|
|
168
191
|
}
|
|
169
192
|
return this.put(id, 'feedback-resolution', resolution);
|
|
170
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* The judgement looked into a topic and chose not to tell the user. This is
|
|
196
|
+
* neither "investigated" (notes) nor "told" (delivery receipts), and it is not
|
|
197
|
+
* a ban: a later message about the topic is still allowed.
|
|
198
|
+
*/
|
|
199
|
+
recordWithheld(entry, id) {
|
|
200
|
+
requireThat(entry &&
|
|
201
|
+
Object.keys(entry).every((k) => ['topicId', 'reason'].includes(k)) &&
|
|
202
|
+
nonempty(entry.topicId) &&
|
|
203
|
+
nonempty(entry.reason), 'Withheld needs a topic and a reason');
|
|
204
|
+
requireThat(this.topics().has(entry.topicId), 'Unknown withheld topic');
|
|
205
|
+
return this.put(id, 'withheld', entry);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* A closed or deferred topic becomes open again because its underlying
|
|
209
|
+
* information changed, not because the user said so. The change must be in the
|
|
210
|
+
* notebook: a note on this topic recorded after the user feedback that governs
|
|
211
|
+
* it. The user's own later feedback still overrides this.
|
|
212
|
+
*/
|
|
213
|
+
reopenTopic(entry, id) {
|
|
214
|
+
requireThat(entry &&
|
|
215
|
+
Object.keys(entry).every((k) => ['topicId', 'reason'].includes(k)) &&
|
|
216
|
+
nonempty(entry.topicId) &&
|
|
217
|
+
nonempty(entry.reason), 'Reopening needs a topic and a reason');
|
|
218
|
+
requireThat(this.topics().has(entry.topicId), 'Unknown reopened topic');
|
|
219
|
+
const current = this.state(entry.topicId);
|
|
220
|
+
requireThat(current.feedbackId && ['closed', 'deferred'].includes(current.status), 'Only a topic the user closed or deferred can be reopened');
|
|
221
|
+
const governing = this.event(current.feedbackId).seq;
|
|
222
|
+
requireThat(this.events('note').some((e) => e.payload.topicId === entry.topicId && e.seq > governing), 'Reopening needs a note on this topic recorded after the user feedback');
|
|
223
|
+
// Bind the reopening to the rule it was validated against (see state()).
|
|
224
|
+
return this.put(id, 'reopen', {
|
|
225
|
+
...entry,
|
|
226
|
+
feedbackId: current.feedbackId,
|
|
227
|
+
ruleIndex: current.ruleIndex,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
171
230
|
commitDraft({ role, operationId, expectedFeedbackRevision, draft, }) {
|
|
172
231
|
requireThat(['exploration', 'judgement'].includes(role), 'Unknown role');
|
|
173
232
|
const digest = fingerprint({ role, expectedFeedbackRevision, draft });
|
|
@@ -179,14 +238,17 @@ export class Notebook {
|
|
|
179
238
|
}
|
|
180
239
|
requireThat(this.revision() === expectedFeedbackRevision, 'FEEDBACK_CHANGED: refresh feedback before committing');
|
|
181
240
|
requireThat(draft &&
|
|
182
|
-
Object.keys(draft).every((k) => ['notes', 'feedbackDecisions', 'messages'].includes(k)), 'Unsupported draft field');
|
|
183
|
-
const { notes = [], feedbackDecisions = [], messages = [] } = draft;
|
|
184
|
-
requireThat([notes, feedbackDecisions, messages].every(Array.isArray), 'Draft operations must be arrays');
|
|
185
|
-
requireThat(role === 'judgement' ||
|
|
241
|
+
Object.keys(draft).every((k) => ['notes', 'feedbackDecisions', 'messages', 'withheld', 'reopened'].includes(k)), 'Unsupported draft field');
|
|
242
|
+
const { notes = [], feedbackDecisions = [], messages = [], withheld = [], reopened = [], } = draft;
|
|
243
|
+
requireThat([notes, feedbackDecisions, messages, withheld, reopened].every(Array.isArray), 'Draft operations must be arrays');
|
|
244
|
+
requireThat(role === 'judgement' ||
|
|
245
|
+
(!feedbackDecisions.length && !messages.length && !withheld.length && !reopened.length), 'Exploration only appends knowledge');
|
|
186
246
|
notes.forEach((n, i) => this.appendNote(n, `${operationId}:note:${i}`));
|
|
187
247
|
feedbackDecisions.forEach((f, i) => this.resolveFeedback(f, `${operationId}:feedback:${i}`));
|
|
188
248
|
if (role === 'judgement')
|
|
189
249
|
requireThat(this.pendingFeedback().length === 0, 'Read and interpret pending user feedback first');
|
|
250
|
+
withheld.forEach((w, i) => this.recordWithheld(w, `${operationId}:withheld:${i}`));
|
|
251
|
+
reopened.forEach((r, i) => this.reopenTopic(r, `${operationId}:reopened:${i}`));
|
|
190
252
|
const preparedIds = [];
|
|
191
253
|
messages.forEach((m, i) => {
|
|
192
254
|
requireThat(Object.keys(m).every((k) => ['text', 'topicIds'].includes(k)) &&
|
|
@@ -204,6 +266,8 @@ export class Notebook {
|
|
|
204
266
|
preparedIds,
|
|
205
267
|
notesAdded: notes.length,
|
|
206
268
|
feedbackProcessed: feedbackDecisions.length,
|
|
269
|
+
withheldRecorded: withheld.length,
|
|
270
|
+
topicsReopened: reopened.length,
|
|
207
271
|
};
|
|
208
272
|
this.put(operationId, 'draft', { digest, result });
|
|
209
273
|
return result;
|
|
@@ -270,13 +334,15 @@ export class Notebook {
|
|
|
270
334
|
deferred: '暂缓',
|
|
271
335
|
closed: '关闭',
|
|
272
336
|
};
|
|
273
|
-
lines.push('', `## ${topic.title}〔${topic.id}〕`, `状态:${labels[s.displayStatus]}${s.reason ? ';原因:' + s.reason : ''}${s.feedbackId ? ';依据:' + s.feedbackId : ''}`, `方向:${topic.tags.join('、') || '未分类'}`);
|
|
337
|
+
lines.push('', `## ${topic.title}〔${topic.id}〕`, `状态:${labels[s.displayStatus]}${s.reopenedByChange ? '(因信息出现重大变化,经判断重新开放)' : ''}${s.reason ? ';原因:' + s.reason : ''}${s.feedbackId ? ';依据:' + s.feedbackId : ''}`, `方向:${topic.tags.join('、') || '未分类'}`);
|
|
274
338
|
if (s.resumeAt)
|
|
275
339
|
lines.push(`用户指定恢复时间:${s.resumeAt}`);
|
|
276
340
|
for (const n of topic.notes)
|
|
277
341
|
lines.push(`- ${n.text}\n 发生时间:${n.occurredAt ?? '未知'}|记录时间:${n.recordedAt}\n 来源:${n.sources.join(';')}${n.related?.length ? '\n 关联主题:' + n.related.join('、') : ''}`);
|
|
278
342
|
for (const r of s.receipts)
|
|
279
343
|
lines.push(`- 已发送 ${r.at}〔${r.id}〕:${r.payload.text}`);
|
|
344
|
+
for (const w of s.withheld)
|
|
345
|
+
lines.push(`- 已调查但未告知用户 ${w.at}〔${w.id}〕:${w.payload.reason}`);
|
|
280
346
|
}
|
|
281
347
|
const quickDeliveries = this.events('delivery').filter((e) => e.payload.origin === 'quick-discovery' && !e.payload.topicIds.length);
|
|
282
348
|
if (quickDeliveries.length) {
|
|
@@ -46,8 +46,8 @@ export function wakeInput({ role, now, viewPath, outputPath, first = false, }) {
|
|
|
46
46
|
if (!['exploration', 'judgement'].includes(role))
|
|
47
47
|
throw Error('Unknown role');
|
|
48
48
|
const contract = `${role === 'judgement' ? '基于用户的全局信息和整份记事本自主调查并判断,不只看新内容或本轮增量。' : '基于用户的全局信息探索,用工具读取笔记并按需自主调查,不只看新内容或本轮增量。'}完成后把增量操作写入 ${outputPath},格式:
|
|
49
|
-
{"notes":[{"topicId":"已有主题ID或新ID","title":"主题名","text":"简短认识","occurredAt":null,"sources":["信息来源"],"tags":["方向标签"],"related":["可选的相关主题ID"]}],"feedbackDecisions":[{"feedbackId":"用户反馈ID","understanding":"对反馈的理解","rules":[{"quote":"反馈原话中的依据","status":"open|deferred|closed","reason":"原因","resumeAt":null,"topicIds":["作用主题"],"tags":["作用方向"]}]}],"messages":[{"text":"完整消息","topicIds":["涉及主题"]}]}。
|
|
50
|
-
occurredAt可写已知的发生时间或范围,不知道就null。无需填满各数组;输出只包含本次增量,不复制整本笔记。主题和方向标签可按内容新增,相关用户反馈仍需考虑。${role === 'exploration' ? '只填写notes
|
|
49
|
+
{"notes":[{"topicId":"已有主题ID或新ID","title":"主题名","text":"简短认识","occurredAt":null,"sources":["信息来源"],"tags":["方向标签"],"related":["可选的相关主题ID"]}],"feedbackDecisions":[{"feedbackId":"用户反馈ID","understanding":"对反馈的理解","rules":[{"quote":"反馈原话中的依据","status":"open|deferred|closed","reason":"原因","resumeAt":null,"topicIds":["作用主题"],"tags":["作用方向"]}]}],"messages":[{"text":"完整消息","topicIds":["涉及主题"]}],"withheld":[{"topicId":"已调查但决定不告知用户的主题ID","reason":"不告知的原因"}],"reopened":[{"topicId":"用户已关闭或暂缓的主题ID","reason":"信息出现了什么重大变化"}]}。
|
|
50
|
+
occurredAt可写已知的发生时间或范围,不知道就null。无需填满各数组;输出只包含本次增量,不复制整本笔记。主题和方向标签可按内容新增,相关用户反馈仍需考虑。${role === 'exploration' ? '只填写notes,其余数组留空。' : '没有待整理反馈时feedbackDecisions留空;只有用户明确表达状态变化才写rules,否则记理解和有价值的信息即可。关闭方向可能涉及多个主题,方向标签可同时作用于以后相关主题。调查过某主题但决定不告知时写withheld,与已告知分开记录;只有当用户已关闭或暂缓的主题出现重大变化(先把变化按记事本规则追加为该主题的新笔记)时才写reopened,没有重大变化就保持用户的决定。'}
|
|
51
51
|
提交与送达以当前记事本记录为准,写过结果文件不代表已提交或已发送。最终回复结果文件路径。`;
|
|
52
52
|
return `${first ? (role === 'exploration' ? DISCOVERY_TASK : JUDGEMENT_TASK) + '\n\n' + NOTEBOOK_RULES + '\n\n' : ''}当前 UTC 时间:${now}\n记事本:${viewPath}\n${contract}`;
|
|
53
53
|
}
|
|
@@ -350,9 +350,14 @@ export class QuickDiscoveryController {
|
|
|
350
350
|
}
|
|
351
351
|
async run(agent, state) {
|
|
352
352
|
const active = state.active;
|
|
353
|
+
// Observation only (#6309): the model window of this attempt, when it ran one.
|
|
354
|
+
const startedAt = Date.now();
|
|
355
|
+
let modelFrom;
|
|
356
|
+
let outcome = 'failed';
|
|
353
357
|
try {
|
|
354
358
|
this.forward();
|
|
355
359
|
if (!active.executionCompleted) {
|
|
360
|
+
modelFrom = Date.now();
|
|
356
361
|
this.parent.refreshNotebook();
|
|
357
362
|
const version = hash(QUICK_DISCOVERY_TASK);
|
|
358
363
|
const input = quickWakeInput(active.from, active.to, this.state().promptVersion !== version);
|
|
@@ -381,6 +386,7 @@ export class QuickDiscoveryController {
|
|
|
381
386
|
});
|
|
382
387
|
}
|
|
383
388
|
this.forward();
|
|
389
|
+
outcome = this.state()?.active?.reports?.length ? 'reported' : 'no_report';
|
|
384
390
|
this.store.update((s) => {
|
|
385
391
|
if (s.active?.id !== active.id)
|
|
386
392
|
throw Error('Quick wake ownership changed');
|
|
@@ -410,6 +416,16 @@ export class QuickDiscoveryController {
|
|
|
410
416
|
}
|
|
411
417
|
finally {
|
|
412
418
|
this.agent = undefined;
|
|
419
|
+
// A retry that only redelivers an earlier report ran no model: not a run.
|
|
420
|
+
if (modelFrom !== undefined)
|
|
421
|
+
await this.ctx.runCost?.record({
|
|
422
|
+
kind: 'quick_discovery',
|
|
423
|
+
runId: active.id,
|
|
424
|
+
startedAt,
|
|
425
|
+
endedAt: Date.now(),
|
|
426
|
+
outcome,
|
|
427
|
+
segments: [{ sessionId: agent.sessionId, from: modelFrom, to: Date.now() }],
|
|
428
|
+
});
|
|
413
429
|
}
|
|
414
430
|
}
|
|
415
431
|
async close() {
|
|
@@ -141,11 +141,16 @@ export class MatterController {
|
|
|
141
141
|
const activationId = m.activation.id;
|
|
142
142
|
let queued = false;
|
|
143
143
|
let failureKind;
|
|
144
|
+
// Observation only (#6309): undefined means no model execution was admitted.
|
|
145
|
+
const startedAt = m.activation.startedAt;
|
|
146
|
+
let outcome = 'failed';
|
|
147
|
+
let retried = false;
|
|
144
148
|
try {
|
|
145
149
|
if (!alreadyStarted) {
|
|
146
150
|
const result = await agent.followup(buildMatterPrompt(this.store.workspace(m.id, activationId)));
|
|
147
151
|
if (result.disposition === 'blocked') {
|
|
148
152
|
this.store.deferAdmission(m.id, activationId, 'Host 暂未接收唤醒,将自动重试。');
|
|
153
|
+
outcome = undefined;
|
|
149
154
|
return;
|
|
150
155
|
}
|
|
151
156
|
if (result.disposition === 'turn_started') {
|
|
@@ -180,6 +185,8 @@ export class MatterController {
|
|
|
180
185
|
failureKind = failure?.kind;
|
|
181
186
|
if (failure?.retryable) {
|
|
182
187
|
this.store.retryExecution(m.id, activationId, failure);
|
|
188
|
+
outcome = 'retried';
|
|
189
|
+
retried = true;
|
|
183
190
|
return;
|
|
184
191
|
}
|
|
185
192
|
throw Error(failure?.message ??
|
|
@@ -195,6 +202,11 @@ export class MatterController {
|
|
|
195
202
|
await completion();
|
|
196
203
|
if (isPendingActivationTurnId(this.store.get(m.id).matter.activation?.turnId))
|
|
197
204
|
throw new Error('Queued wake ended without claiming its activation; inspect conversation before resuming');
|
|
205
|
+
const live = this.store.get(m.id).matter.activation;
|
|
206
|
+
// A retry already decided the outcome: it cleared the activation on purpose,
|
|
207
|
+
// so do not read that as the user interrupting the run.
|
|
208
|
+
if (!retried)
|
|
209
|
+
outcome = live?.id !== activationId ? 'interrupted' : live.settled ? 'completed' : 'failed';
|
|
198
210
|
this.store.finish(m.id, activationId);
|
|
199
211
|
}
|
|
200
212
|
catch (e) {
|
|
@@ -204,6 +216,7 @@ export class MatterController {
|
|
|
204
216
|
isPendingActivationTurnId(live.activation.turnId) &&
|
|
205
217
|
/Runtime Host is not ready for Plugin Agent messages/.test(String(e))) {
|
|
206
218
|
this.store.deferAdmission(m.id, activationId, String(e));
|
|
219
|
+
outcome = undefined;
|
|
207
220
|
return;
|
|
208
221
|
}
|
|
209
222
|
if (live.activation?.id === activationId) {
|
|
@@ -212,6 +225,19 @@ export class MatterController {
|
|
|
212
225
|
await agent.cancel().catch(() => { });
|
|
213
226
|
}
|
|
214
227
|
}
|
|
228
|
+
finally {
|
|
229
|
+
// The ledger swallows its own failures and nothing reads it back.
|
|
230
|
+
if (outcome)
|
|
231
|
+
await this.ctx.runCost?.record({
|
|
232
|
+
kind: 'matter_activation',
|
|
233
|
+
runId: activationId,
|
|
234
|
+
matterId: m.id,
|
|
235
|
+
startedAt,
|
|
236
|
+
endedAt: Date.now(),
|
|
237
|
+
outcome,
|
|
238
|
+
segments: [{ sessionId: m.sessionId, from: startedAt, to: Date.now() }],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
215
241
|
}
|
|
216
242
|
async enroll(input, call) {
|
|
217
243
|
this.assertReady();
|