@kingsoftcloud/agentkit-harness-node 0.0.0-stage → 0.0.2
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/LICENSE +201 -0
- package/NOTICE +1 -0
- package/README.md +124 -2
- package/THIRD_PARTY_NOTICES.md +66 -0
- package/ZCode-LICENSE +202 -0
- package/dist/index.mjs +4871 -0
- package/dist/native-command-plan.LICENSE.txt +221 -0
- package/dist/native-command-plan.json +16 -0
- package/dist/native-command-plan.mjs +8 -0
- package/dist/rpc-entry.mjs +5682 -0
- package/package.json +169 -4
- package/src/native/a2a-receipt-source.mjs +19 -0
- package/src/native/a2a-tools.mjs +517 -0
- package/src/native/activity-status.mjs +30 -0
- package/src/native/artifact-codec.mjs +481 -0
- package/src/native/artifact-store.mjs +1162 -0
- package/src/native/assistant-stream.mjs +76 -0
- package/src/native/background-command-port.mjs +95 -0
- package/src/native/browser-action-observation.mjs +43 -0
- package/src/native/browser-availability.mjs +22 -0
- package/src/native/browser-canvas-metrics.mjs +71 -0
- package/src/native/browser-dom.mjs +193 -0
- package/src/native/browser-playwright.mjs +1249 -0
- package/src/native/browser-vision.mjs +369 -0
- package/src/native/browser-workspace-policy.mjs +22 -0
- package/src/native/browser-workspace-protocol.mjs +95 -0
- package/src/native/browser-workspace-server.mjs +164 -0
- package/src/native/browser-workspace.mjs +355 -0
- package/src/native/captured-source.mjs +3 -0
- package/src/native/child-budget-allocation.mjs +87 -0
- package/src/native/child-capabilities.mjs +35 -0
- package/src/native/child-command-resource.mjs +20 -0
- package/src/native/child-history.mjs +188 -0
- package/src/native/child-merge.mjs +98 -0
- package/src/native/child-model-surface.mjs +19 -0
- package/src/native/child-observation.mjs +100 -0
- package/src/native/child-owner.mjs +620 -0
- package/src/native/child-publication.mjs +76 -0
- package/src/native/child-readers.mjs +366 -0
- package/src/native/child-tools.mjs +46 -0
- package/src/native/child-workspace-binding.mjs +29 -0
- package/src/native/child-workspace-tools.mjs +1 -0
- package/src/native/child-workspaces.mjs +655 -0
- package/src/native/children.mjs +365 -0
- package/src/native/command-plan-worker.mjs +14 -0
- package/src/native/command-plan.mjs +31 -0
- package/src/native/command-progress.mjs +64 -0
- package/src/native/command-shell.mjs +9 -0
- package/src/native/command-workspace-observation.mjs +36 -0
- package/src/native/component-tools.mjs +280 -0
- package/src/native/context-counter.mjs +133 -0
- package/src/native/context-markers.mjs +51 -0
- package/src/native/context-profile.mjs +72 -0
- package/src/native/context-status.mjs +27 -0
- package/src/native/conversation-command-control.mjs +133 -0
- package/src/native/delivery-timeline.mjs +39 -0
- package/src/native/dsh-background-ownership.mjs +102 -0
- package/src/native/dsh-managed-ownership.mjs +23 -0
- package/src/native/dsh-turn-contract.mjs +3 -0
- package/src/native/dsh-turn-ownership.mjs +54 -0
- package/src/native/dsh-turn-runtime.mjs +498 -0
- package/src/native/e2b-background-supervisor.mjs +347 -0
- package/src/native/e2b-background.mjs +368 -0
- package/src/native/e2b-command-state.mjs +40 -0
- package/src/native/e2b-command-tools.mjs +486 -0
- package/src/native/e2b-sandbox-owner.mjs +227 -0
- package/src/native/hook-executor.mjs +249 -0
- package/src/native/instruction-assembly.mjs +60 -0
- package/src/native/instruction-context.mjs +54 -0
- package/src/native/local-background-supervisor.mjs +108 -0
- package/src/native/local-background.mjs +207 -0
- package/src/native/local-command-evidence.mjs +57 -0
- package/src/native/local-command-policy-worker.mjs +197 -0
- package/src/native/local-command-policy.mjs +46 -0
- package/src/native/local-command-state.mjs +177 -0
- package/src/native/local-command-tools.mjs +302 -0
- package/src/native/local-execution-profile.mjs +36 -0
- package/src/native/local-output-policy.mjs +92 -0
- package/src/native/local-process-bin/darwin-arm64/launcher +0 -0
- package/src/native/local-process-bin/darwin-x64/launcher +0 -0
- package/src/native/local-process-bin/linux-arm64/launcher +0 -0
- package/src/native/local-process-bin/linux-x64/launcher +0 -0
- package/src/native/local-process-launcher.c +183 -0
- package/src/native/local-process-licenses/GCC-COPYING.RUNTIME +72 -0
- package/src/native/local-process-licenses/GCC-COPYING3 +674 -0
- package/src/native/local-process-licenses/NOTICE +21 -0
- package/src/native/local-process-licenses/musl-COPYRIGHT +193 -0
- package/src/native/local-process-manifest.json +32 -0
- package/src/native/local-process.mjs +246 -0
- package/src/native/local-resource-profile.mjs +32 -0
- package/src/native/memory-candidate.mjs +121 -0
- package/src/native/memory-extraction-run.mjs +60 -0
- package/src/native/memory-extraction-tools.mjs +116 -0
- package/src/native/memory-recall.mjs +65 -0
- package/src/native/memory-store.mjs +334 -0
- package/src/native/memory-tools.mjs +329 -0
- package/src/native/model-anthropic-cache.mjs +49 -0
- package/src/native/model-anthropic-tool-search.mjs +85 -0
- package/src/native/model-anthropic.mjs +753 -0
- package/src/native/model-chat-usage.mjs +24 -0
- package/src/native/model-inline-reasoning.mjs +111 -0
- package/src/native/model-media.mjs +174 -0
- package/src/native/model-normalize.mjs +228 -0
- package/src/native/model-openai-chat.mjs +561 -0
- package/src/native/model-port.mjs +361 -0
- package/src/native/model-provider-compat.mjs +129 -0
- package/src/native/model-replay.mjs +153 -0
- package/src/native/model-request.mjs +15 -0
- package/src/native/model-responses.mjs +1205 -0
- package/src/native/model-sse.mjs +171 -0
- package/src/native/model-surface-host.mjs +27 -0
- package/src/native/model-tool-search-policy.mjs +16 -0
- package/src/native/model-transport-observation.mjs +27 -0
- package/src/native/owned-child.mjs +114 -0
- package/src/native/owned-command.mjs +106 -0
- package/src/native/python-syntax-worker.mjs +60 -0
- package/src/native/python-syntax.mjs +50 -0
- package/src/native/read-interruption.mjs +16 -0
- package/src/native/result-stream.mjs +63 -0
- package/src/native/ruff-ast.mjs +95 -0
- package/src/native/run-observation.mjs +128 -0
- package/src/native/session-delivery-journal.mjs +96 -0
- package/src/native/session-delivery.mjs +434 -0
- package/src/native/shell-analysis.mjs +50 -0
- package/src/native/skill-disclosure.mjs +404 -0
- package/src/native/skill-events.mjs +164 -0
- package/src/native/skill-tools.mjs +813 -0
- package/src/native/source-page.mjs +73 -0
- package/src/native/sqlite-session-inbox.mjs +42 -0
- package/src/native/sqlite-store.mjs +617 -0
- package/src/native/tool-authority.mjs +99 -0
- package/src/native/tool-guidance.mjs +12 -0
- package/src/native/tool-observation.mjs +75 -0
- package/src/native/tool-states.mjs +14 -0
- package/src/native/verification-authority.mjs +37 -0
- package/src/native/web-tools.mjs +643 -0
- package/src/native/workspace-checkpoints.mjs +312 -0
- package/src/native/workspace-context.mjs +61 -0
- package/src/native/workspace-display.mjs +39 -0
- package/src/native/workspace-edit-match.mjs +19 -0
- package/src/native/workspace-edit-worker.mjs +15 -0
- package/src/native/workspace-editing.mjs +447 -0
- package/src/native/workspace-image.mjs +72 -0
- package/src/native/workspace-instructions.mjs +107 -0
- package/src/native/workspace-mutation-lock.mjs +74 -0
- package/src/native/workspace-patch-worker.mjs +17 -0
- package/src/native/workspace-patch.mjs +17 -0
- package/src/native/workspace-paths.mjs +225 -0
- package/src/native/workspace-state.mjs +272 -0
- package/src/native/workspace-subject-observer.mjs +84 -0
- package/src/native/workspace-tools.mjs +1740 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { nativeChildObservation, nativeChildActivity } from './child-observation.mjs';
|
|
2
|
+
import { nativeDeliveryId } from './delivery-timeline.mjs';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { addUsage, canonical, copy, invariant, scopeKey, terminal, CHILD_OWNER_CONTRACT } from '@kingsoftcloud/agentkit-harness-core';
|
|
5
|
+
import { accountedChildDelivery, loadOwnedDirectChild } from './owned-child.mjs';
|
|
6
|
+
|
|
7
|
+
export const NATIVE_CHILD_HISTORY_CONTRACT = 'agentkit.studio-native-child-history/v1';
|
|
8
|
+
const hash = value => createHash('sha256').update(canonical(value)).digest('hex');
|
|
9
|
+
const id = value => typeof value === 'string' && value.trim() && value.length <= 256;
|
|
10
|
+
|
|
11
|
+
function pageInventory(request, inventory) {
|
|
12
|
+
const offset = request.offset ?? 0, limit = request.limit ?? 16;
|
|
13
|
+
invariant(Number.isSafeInteger(offset) && offset >= 0 && Number.isSafeInteger(limit) && limit >= 1 && limit <= 64
|
|
14
|
+
&& (request.inventoryDigest === undefined || typeof request.inventoryDigest === 'string'
|
|
15
|
+
&& /^[0-9a-f]{64}$/.test(request.inventoryDigest)), 'child_history_query_invalid');
|
|
16
|
+
invariant((!offset || request.inventoryDigest)
|
|
17
|
+
&& (request.inventoryDigest === undefined || request.inventoryDigest === inventory.inventoryDigest), 'child_history_inventory_changed');
|
|
18
|
+
invariant(offset <= inventory.tasks.length, 'child_history_offset_invalid');
|
|
19
|
+
const tasks = inventory.tasks.slice(offset, offset + limit), nextOffset = offset + tasks.length;
|
|
20
|
+
return { ...inventory, tasks, totalTasks: inventory.tasks.length, offset, nextOffset, complete: nextOffset === inventory.tasks.length };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Combine inventories supplied by trusted owners, never caller-selected stores
|
|
24
|
+
* or principals. Keep each owner's ancestry order; the current owner comes first.
|
|
25
|
+
* The digest binds every source, including heads without children. */
|
|
26
|
+
export function mergeNativeChildHistory(request, inventories) {
|
|
27
|
+
invariant(id(request.sessionId) && request.runId === undefined && Array.isArray(inventories) && inventories.length > 0,
|
|
28
|
+
'child_history_query_invalid');
|
|
29
|
+
invariant(inventories.every(value => value.contract === NATIVE_CHILD_HISTORY_CONTRACT
|
|
30
|
+
&& value.sessionId === request.sessionId && (value.runId === null || id(value.runId)) && Array.isArray(value.tasks)),
|
|
31
|
+
'child_history_ancestry_invalid');
|
|
32
|
+
const present = inventories.filter(value => value.runId !== null);
|
|
33
|
+
if (present.length <= 1) return pageInventory(request, present[0] ?? inventories[0]);
|
|
34
|
+
const tasks = present.flatMap(value => value.tasks), runId = present[0].runId;
|
|
35
|
+
// A public child ID must resolve to exactly one owner. Never silently choose
|
|
36
|
+
// one ledger on collision or copy controls onto another owner's history.
|
|
37
|
+
invariant(new Set(tasks.map(task => task.childRunId)).size === tasks.length, 'child_history_ancestry_invalid');
|
|
38
|
+
const parents = new Set();
|
|
39
|
+
for (const source of present) {
|
|
40
|
+
const sourceParents = new Set([source.runId, ...source.tasks.map(task => task.parentRunId)]);
|
|
41
|
+
invariant([...sourceParents].every(parent => !parents.has(parent)), 'child_history_ancestry_invalid');
|
|
42
|
+
for (const parent of sourceParents) parents.add(parent);
|
|
43
|
+
}
|
|
44
|
+
const selected = tasks.map(task => task.parentRunId === runId ? task
|
|
45
|
+
: { ...task, canPromote: false, canCancel: false, canSendInput: false });
|
|
46
|
+
return pageInventory(request, { contract: NATIVE_CHILD_HISTORY_CONTRACT, sessionId: request.sessionId, runId,
|
|
47
|
+
inventoryDigest: hash({ contract: 'agentkit.native-child-inventory-merge/v1', sessionId: request.sessionId,
|
|
48
|
+
sources: present.map(value => [value.runId, value.inventoryDigest]) }), tasks: selected });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Read-only product history. No Kernel, factories, leases, commands or accounting writes. */
|
|
52
|
+
export function createNativeChildHistory({ scope, store, liveTasks = () => [], liveObservation = () => null }) {
|
|
53
|
+
scopeKey(scope);
|
|
54
|
+
const hierarchy = request => {
|
|
55
|
+
invariant(id(request.sessionId) && (request.runId === undefined || id(request.runId)), 'child_history_query_invalid');
|
|
56
|
+
const head = store.sessionHead(scope, request.sessionId), parents = [], seen = new Set();
|
|
57
|
+
if (!head) {
|
|
58
|
+
invariant(request.runId === undefined, 'child_history_run_unavailable');
|
|
59
|
+
return { head: null, parents };
|
|
60
|
+
}
|
|
61
|
+
const authority = runId => store.authority?.(scope, runId) ?? store.load(scope, runId);
|
|
62
|
+
let parent = authority(head.runId), cursor = parent.snapshot;
|
|
63
|
+
while (cursor) {
|
|
64
|
+
invariant(!seen.has(cursor.runId) && cursor.sessionId === request.sessionId
|
|
65
|
+
&& canonical(cursor.scope) === canonical(scope), 'child_history_ancestry_invalid');
|
|
66
|
+
seen.add(cursor.runId);
|
|
67
|
+
invariant(hash(parent.configuration) === cursor.configDigest, 'stored_configuration_mismatch');
|
|
68
|
+
parents.push(parent);
|
|
69
|
+
if (!cursor.parentRunId) break;
|
|
70
|
+
parent = authority(cursor.parentRunId); cursor = parent.snapshot;
|
|
71
|
+
invariant(terminal(cursor.phase), 'child_history_ancestry_invalid');
|
|
72
|
+
}
|
|
73
|
+
if (request.runId !== undefined) {
|
|
74
|
+
const index = parents.findIndex(parent => parent.snapshot.runId === request.runId);
|
|
75
|
+
invariant(index >= 0, 'child_history_run_unavailable');
|
|
76
|
+
return { head: parents[index].snapshot, parents: parents.slice(index) };
|
|
77
|
+
}
|
|
78
|
+
return { head, parents };
|
|
79
|
+
};
|
|
80
|
+
const inventory = request => {
|
|
81
|
+
const { head, parents } = hierarchy(request), records = [];
|
|
82
|
+
for (const view of parents) {
|
|
83
|
+
const active = view.snapshot.runId === head?.runId ? liveTasks(view.snapshot.runId) : [];
|
|
84
|
+
const roles = [...new Set(view.configuration.tools.filter(tool => ['start/v1', 'delegate/v1'].includes(tool.childBudgetPolicy))
|
|
85
|
+
.flatMap(tool => tool.parameters.properties?.role?.enum ?? []))];
|
|
86
|
+
invariant(roles.every(role => typeof role === 'string' && /^[a-z0-9][a-z0-9_-]{0,63}$/.test(role)), 'child_history_roles_invalid');
|
|
87
|
+
// Browsing an empty child directory needs lineage authority, not every
|
|
88
|
+
// historical model result and screenshot. Only actual child observations
|
|
89
|
+
// materialize the journal needed to authenticate their settled delivery.
|
|
90
|
+
if (!(view.snapshot.childRunIds ?? []).length && !active.length) continue;
|
|
91
|
+
const parent = store.load(scope, view.snapshot.runId);
|
|
92
|
+
invariant(parent.snapshot.sessionId === request.sessionId
|
|
93
|
+
&& parent.snapshot.configDigest === view.snapshot.configDigest
|
|
94
|
+
&& canonical(parent.snapshot.scope) === canonical(scope), 'child_history_ancestry_invalid');
|
|
95
|
+
// Historical grants come from the frozen original tool, never current
|
|
96
|
+
// model arguments or a caller-provided child scope/role.
|
|
97
|
+
const childScopes = roles.map(role => ({ ...scope, agentId: `${scope.agentId}:child:${role}` }));
|
|
98
|
+
const children = [], descendants = new Set();
|
|
99
|
+
for (const childRunId of parent.snapshot.childRunIds ?? []) {
|
|
100
|
+
const child = loadOwnedDirectChild({ store, childScopes, parent: parent.snapshot, childRunId, roles });
|
|
101
|
+
if (child) { children.push(child); for (const runId of child.snapshot.childRunIds ?? []) descendants.add(runId); }
|
|
102
|
+
}
|
|
103
|
+
const direct = new Set(children.map(child => child.snapshot.runId));
|
|
104
|
+
invariant((parent.snapshot.childRunIds ?? []).every(runId => direct.has(runId) || descendants.has(runId)), 'child_history_not_available');
|
|
105
|
+
// The owning Kernel can authenticate a start before its parent receipt
|
|
106
|
+
// commits. Such a row has controls, but never a settled delivery.
|
|
107
|
+
for (const task of active) if (!direct.has(task.childRunId)) {
|
|
108
|
+
const childScope = childScopes.find(item => item.agentId === `${scope.agentId}:child:${task.role}`);
|
|
109
|
+
invariant(childScope, 'child_history_not_available');
|
|
110
|
+
const child = store.load(childScope, task.childRunId);
|
|
111
|
+
invariant(hash(child.configuration) === child.snapshot.configDigest, 'stored_configuration_mismatch');
|
|
112
|
+
children.push(child); direct.add(task.childRunId);
|
|
113
|
+
}
|
|
114
|
+
for (const child of children) records.push({ parent, child, control: active.find(task => task.childRunId === child.snapshot.runId) });
|
|
115
|
+
}
|
|
116
|
+
return { head, records };
|
|
117
|
+
};
|
|
118
|
+
const taskRow = ({ parent, child, control }) => {
|
|
119
|
+
const snapshot = child.snapshot, binding = child.configuration.childOwnerBinding;
|
|
120
|
+
const accounted = Boolean(accountedChildDelivery(parent, child));
|
|
121
|
+
const command = parent.commands.filter(row => ['message_child', 'cancel_child', 'promote_child'].includes(row.command.kind)
|
|
122
|
+
&& row.command.payload.childRunId === snapshot.runId).at(-1);
|
|
123
|
+
const applied = command && parent.events.find(event => event.kind === 'command.applied' && event.payload.commandId === command.command.commandId);
|
|
124
|
+
const lastControl = command ? { commandId: command.command.commandId, action: command.command.kind,
|
|
125
|
+
status: !command.consumed ? 'pending' : ['queued', 'applied'].includes(applied?.payload.reason) ? 'delivered' : 'rejected',
|
|
126
|
+
reason: applied?.payload.reason ?? (command.consumed ? 'parent_run_ended' : null) } : null;
|
|
127
|
+
const a2aTasks = child.intents.filter(intent => intent.kind === 'tool').flatMap(intent => (intent.payload.calls ?? [])
|
|
128
|
+
.filter(call => call.name === 'call_a2a_agent').map(call => ({ runId: parent.snapshot.runId, childRunId: snapshot.runId,
|
|
129
|
+
childAgentId: snapshot.scope.agentId, childSessionId: snapshot.sessionId, operationKey: intent.operationKey, callId: call.callId })));
|
|
130
|
+
return { parentToolCallId: nativeDeliveryId(parent.snapshot.scope, parent.snapshot.runId, `call:${binding.parentToolCallId}`),
|
|
131
|
+
childRunId: snapshot.runId, sessionId: snapshot.sessionId, parentRunId: parent.snapshot.runId,
|
|
132
|
+
...(a2aTasks.length ? { a2aTasks } : {}),
|
|
133
|
+
role: binding.role, phase: snapshot.phase, outcome: snapshot.outcome, reason: snapshot.reason,
|
|
134
|
+
activity: nativeChildActivity(child),
|
|
135
|
+
delivery: control?.delivery ?? (binding.deliveryMode === 'foreground'
|
|
136
|
+
&& !(parent.snapshot.promotedChildRunIds ?? []).includes(snapshot.runId) ? 'foreground' : 'background'),
|
|
137
|
+
canPromote: control?.canPromote === true, canCancel: control?.canCancel === true, canSendInput: control?.canSendInput === true,
|
|
138
|
+
goalRevision: snapshot.goalRevision,
|
|
139
|
+
lastControl,
|
|
140
|
+
goalPreview: [...snapshot.goal].slice(0, 256).join(''), goalComplete: [...snapshot.goal].length <= 256,
|
|
141
|
+
snapshotRevision: snapshot.revision, resultAvailable: accounted,
|
|
142
|
+
usage: addUsage(snapshot.usage, snapshot.descendantUsage),
|
|
143
|
+
usageComplete: terminal(snapshot.phase) && snapshot.diagnostics.providerUsageIncomplete !== true && snapshot.diagnostics.descendantUsageIncomplete !== true,
|
|
144
|
+
usageIncludedInParent: accounted, continuationOf: binding.continuationOf ?? null, buildId: snapshot.buildId };
|
|
145
|
+
};
|
|
146
|
+
const readInventory = request => {
|
|
147
|
+
const { head, records } = inventory(request);
|
|
148
|
+
return { contract: NATIVE_CHILD_HISTORY_CONTRACT, sessionId: request.sessionId, runId: head?.runId ?? null,
|
|
149
|
+
inventoryDigest: hash({ scope, sessionId: request.sessionId, runId: head?.runId ?? null,
|
|
150
|
+
children: records.map(({ parent, child }) => [parent.snapshot.runId, child.snapshot.runId]) }), tasks: records.map(taskRow) };
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
// Trusted Host composition only; public transports expose bounded list/result.
|
|
154
|
+
inventory: readInventory,
|
|
155
|
+
// Trusted Host fact lookup. Public list/result responses never expose the
|
|
156
|
+
// full record, and a current parent's lookup cannot select an ancestor's child.
|
|
157
|
+
record(request) {
|
|
158
|
+
invariant(id(request.childRunId), 'child_history_query_invalid');
|
|
159
|
+
const { head, records } = inventory(request);
|
|
160
|
+
const found = records.find(({ parent, child }) => parent.snapshot.runId === head?.runId && child.snapshot.runId === request.childRunId);
|
|
161
|
+
invariant(found, 'child_history_result_unavailable');
|
|
162
|
+
const binding = found.child.configuration.childOwnerBinding, parent = found.parent.snapshot;
|
|
163
|
+
invariant(binding?.contract === CHILD_OWNER_CONTRACT
|
|
164
|
+
&& canonical(binding.parent) === canonical(Object.fromEntries(['scope', 'runId', 'sessionId', 'turnId', 'buildId', 'parentRunId', 'taskChainId']
|
|
165
|
+
.map(key => [key, parent[key]])))
|
|
166
|
+
&& canonical(found.child.snapshot.scope) === canonical({ ...scope, agentId: `${scope.agentId}:child:${binding.role}` }),
|
|
167
|
+
'child_history_ancestry_invalid');
|
|
168
|
+
return copy(found.child);
|
|
169
|
+
},
|
|
170
|
+
list(request) {
|
|
171
|
+
return pageInventory(request, readInventory(request));
|
|
172
|
+
},
|
|
173
|
+
result(request) {
|
|
174
|
+
const offset = request.offset ?? 0, limit = request.limit ?? 4000;
|
|
175
|
+
invariant(id(request.childRunId) && Number.isSafeInteger(offset) && offset >= 0
|
|
176
|
+
&& Number.isSafeInteger(limit) && limit >= 1 && limit <= 16000, 'child_history_query_invalid');
|
|
177
|
+
const { head, records } = inventory(request), found = records.find(({ child }) => child.snapshot.runId === request.childRunId);
|
|
178
|
+
invariant(found, 'child_history_result_unavailable');
|
|
179
|
+
const task = taskRow(found), chars = task.resultAvailable ? [...found.child.snapshot.finalText] : [];
|
|
180
|
+
invariant(offset <= chars.length, 'child_history_offset_invalid');
|
|
181
|
+
const text = chars.slice(offset, offset + limit).join(''), nextOffset = offset + [...text].length;
|
|
182
|
+
return { contract: NATIVE_CHILD_HISTORY_CONTRACT, sessionId: request.sessionId, runId: head.runId,
|
|
183
|
+
observation: nativeChildObservation(found.child, liveObservation(found.child)),
|
|
184
|
+
task, text, totalChars: chars.length, offset, nextOffset, complete: nextOffset === chars.length,
|
|
185
|
+
resultDigest: task.resultAvailable ? hash(found.child.snapshot.finalText) : null, parentAcceptance: 'not_evaluated' };
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { canonical, copy, invariant, toolInputRejection, matchesToolExecutionContract } from '@kingsoftcloud/agentkit-harness-core';
|
|
2
|
+
import { loadSessionChild, accountedChildDelivery } from '@kingsoftcloud/agentkit-harness-node/native/owned-child';
|
|
3
|
+
import { createNativeToolAuthority } from './tool-authority.mjs';
|
|
4
|
+
|
|
5
|
+
export const STUDIO_NATIVE_CHILD_MERGE = 'native.child-file-merge/v2';
|
|
6
|
+
export function nativeChildMergeDefinition() {
|
|
7
|
+
return { name: 'native_child_merge',
|
|
8
|
+
description: 'Merge the isolated files of an accounted completed writing child into the parent workspace. Conflicts publish nothing. Inspect partial or unknown publication before continuing. Child findings and merged files do not verify the parent task.',
|
|
9
|
+
parameters: { type: 'object', additionalProperties: false, required: ['child_run_id'],
|
|
10
|
+
properties: { child_run_id: { type: 'string', minLength: 1, maxLength: 256 } } },
|
|
11
|
+
readOnly: false, concurrentSafe: false, permissions: ['workspace:write'], timeoutMs: 120000,
|
|
12
|
+
contextRole: 'none', observationRole: 'none', progressObservation: false, contextRetention: 'normal' };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Called only inside the admitted Host tool pipeline. The Native ledger is
|
|
16
|
+
* still checked independently, including after the synchronous filesystem lock. */
|
|
17
|
+
export function createStudioNativeChildMerge({ scope, sessionId, store, roles, fileWorkspaces,
|
|
18
|
+
previousPublication, authorize, isClosed = () => false }) {
|
|
19
|
+
invariant(fileWorkspaces && typeof previousPublication === 'function' && typeof authorize === 'function',
|
|
20
|
+
'studio_native_child_merge_binding_missing');
|
|
21
|
+
const boundScope = copy(scope), boundRoles = copy(roles);
|
|
22
|
+
const childScopes = boundRoles.map(role => ({ ...boundScope, agentId: `${boundScope.agentId}:child:${role.name}` }));
|
|
23
|
+
const byRole = new Map(boundRoles.map(role => [role.name, role]));
|
|
24
|
+
const definition = nativeChildMergeDefinition();
|
|
25
|
+
const authority = createNativeToolAuthority({ scope: boundScope, sessionId, store, isClosed });
|
|
26
|
+
const error = code => ({ status: 'error', content: { error_code: code, executed: false } });
|
|
27
|
+
return { definition: copy(definition), contract: STUDIO_NATIVE_CHILD_MERGE,
|
|
28
|
+
async execute(request) {
|
|
29
|
+
const signal = request?.signal;
|
|
30
|
+
const body = () => { const value = { ...request }; delete value.signal; return value; };
|
|
31
|
+
let snapshot;
|
|
32
|
+
try { snapshot = canonical(body()); }
|
|
33
|
+
catch { return error('child_merge_identity_invalid'); }
|
|
34
|
+
let childCurrent = () => true;
|
|
35
|
+
const guard = () => {
|
|
36
|
+
try {
|
|
37
|
+
return request.signal === signal && signal instanceof AbortSignal && !signal.aborted
|
|
38
|
+
&& canonical(body()) === snapshot
|
|
39
|
+
&& matchesToolExecutionContract(request.definition, definition) && authority(request) && childCurrent()
|
|
40
|
+
&& (() => {
|
|
41
|
+
const current = store.load(boundScope, request.runId);
|
|
42
|
+
const allowed = (current.snapshot.taskContract ?? current.configuration.contract).allowedToolNames;
|
|
43
|
+
return allowed == null || Array.isArray(allowed) && allowed.includes(definition.name);
|
|
44
|
+
})();
|
|
45
|
+
} catch { return false; }
|
|
46
|
+
};
|
|
47
|
+
if (!guard()) return error('child_merge_authority_lost');
|
|
48
|
+
const rejection = toolInputRejection(definition, request.call);
|
|
49
|
+
if (rejection) return { status: 'error', content: rejection };
|
|
50
|
+
const original = copy({ scope: request.scope, runId: request.runId, sessionId, operationKey: request.operationKey,
|
|
51
|
+
child_run_id: request.call.arguments.child_run_id });
|
|
52
|
+
let publicationStarted = false;
|
|
53
|
+
const uncertain = (code, applied = []) => ({ status: 'uncertain', workspaceChangedPaths: [...applied],
|
|
54
|
+
content: { schema_version: STUDIO_NATIVE_CHILD_MERGE, error_code: code, ...original, applied: [...applied],
|
|
55
|
+
instruction: 'Inspect the original child and parent files and the saved operation before continuing. Do not repeat an unknown publication automatically.' } });
|
|
56
|
+
try {
|
|
57
|
+
const decision = await authorize({ ...copy(body()), signal });
|
|
58
|
+
if (!guard()) return error('child_merge_authority_lost');
|
|
59
|
+
if (decision !== 'allow') return decision?.status === 'approval_required' && typeof decision.interactionId === 'string'
|
|
60
|
+
&& decision.interactionId ? decision : error('child_merge_permission_denied');
|
|
61
|
+
const parent = store.load(boundScope, request.runId).snapshot;
|
|
62
|
+
const resolved = loadSessionChild({ store, childScopes, parent, childRunId: original.child_run_id });
|
|
63
|
+
if (!resolved || !accountedChildDelivery(resolved.parent, resolved.child)) return error('child_not_accounted');
|
|
64
|
+
const child = resolved.child, childSnapshot = child.snapshot;
|
|
65
|
+
const role = byRole.get(child.configuration.childOwnerBinding?.role);
|
|
66
|
+
const writing = role?.tools.some(name => ['write_workspace_file', 'write_workspace_files', 'edit_workspace_file',
|
|
67
|
+
'multi_edit_workspace_file', 'delete_workspace_file', 'apply_workspace_patch', 'rewind_workspace_files'].includes(name));
|
|
68
|
+
const head = store.sessionHead(childSnapshot.scope, childSnapshot.sessionId);
|
|
69
|
+
if (!writing || role.concurrentSafe || childSnapshot.phase !== 'completed'
|
|
70
|
+
|| ['failed', 'blocked', 'budget_exhausted'].includes(childSnapshot.outcome)
|
|
71
|
+
|| head?.runId !== childSnapshot.runId || !childSnapshot.workspaceRevision) return error('child_merge_unavailable');
|
|
72
|
+
const childSeal = canonical(childSnapshot);
|
|
73
|
+
childCurrent = () => store.sessionHead(childSnapshot.scope, childSnapshot.sessionId)?.runId === childSnapshot.runId
|
|
74
|
+
&& canonical(store.load(childSnapshot.scope, childSnapshot.runId).snapshot) === childSeal;
|
|
75
|
+
const expectedBinding = child.configuration.hostRecoveryBinding?.childWorkspace;
|
|
76
|
+
if (!expectedBinding) return error('child_workspace_unbound');
|
|
77
|
+
const bound = fileWorkspaces.bind({ scope: childSnapshot.scope, sessionId: childSnapshot.sessionId, role,
|
|
78
|
+
existing: true, expectedBinding });
|
|
79
|
+
const previous = await previousPublication({ store, childScopes, parent, child });
|
|
80
|
+
if (!guard()) return error('child_merge_authority_lost');
|
|
81
|
+
// The directory publisher owns all remaining synchronous work and checks
|
|
82
|
+
// the live intent/fence again after acquiring every shared lock.
|
|
83
|
+
publicationStarted = true;
|
|
84
|
+
const result = fileWorkspaces.files.merge(bound.workspace, {
|
|
85
|
+
expectedRevision: childSnapshot.workspaceRevision, previous, assertCurrent: guard });
|
|
86
|
+
if (!guard()) return uncertain('child_merge_authority_lost', result.applied);
|
|
87
|
+
if (!result.ok && result.error_code === 'child_workspace_publish_failed')
|
|
88
|
+
return uncertain(result.error_code, result.applied);
|
|
89
|
+
return { status: result.ok ? 'ok' : 'error', workspaceChangedPaths: [...result.applied],
|
|
90
|
+
content: { schema_version: STUDIO_NATIVE_CHILD_MERGE, child_run_id: childSnapshot.runId, ...result } };
|
|
91
|
+
} catch (failure) {
|
|
92
|
+
if (failure?.effectStarted || publicationStarted && failure?.code !== 'child_workspace_guard_failed')
|
|
93
|
+
return uncertain(failure?.code || 'child_merge_publication_unknown', failure?.applied);
|
|
94
|
+
return error(failure?.code || 'child_workspace_merge_unavailable');
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { validateModelSurface } from '@kingsoftcloud/agentkit-harness-core';
|
|
2
|
+
|
|
3
|
+
/** A child gets facts for its own workspace and selected model, with its narrower tool catalog. */
|
|
4
|
+
export function nativeChildModelSurface(parent, { workspaceRoot, supportsImages, tools, original } = {}) {
|
|
5
|
+
if (!parent) return undefined;
|
|
6
|
+
const { commandCwd: _cwd, environmentNames: _names, commandResources: _resources, ...base } = parent.environment;
|
|
7
|
+
const root = workspaceRoot ?? base.workspaceRoot, commands = tools.some(tool => tool.name === 'run_command');
|
|
8
|
+
const environment = { ...base, workspaceRoot: root, cwd: root, supportsImages,
|
|
9
|
+
date: original?.environment.date ?? base.date, commandBackend: commands ? base.commandBackend : 'none',
|
|
10
|
+
...(commands && base.commandBackend === 'local_process' ? { commandCwd: root,
|
|
11
|
+
environmentNames: parent.environment.environmentNames ?? [],
|
|
12
|
+
...(parent.environment.commandResources ? { commandResources: structuredClone(parent.environment.commandResources) } : {}) } : {}) };
|
|
13
|
+
const source=original??parent;
|
|
14
|
+
const result = { contract: parent.contract, environment,
|
|
15
|
+
...(parent.executionCapabilities === undefined ? {} : { executionCapabilities: parent.executionCapabilities
|
|
16
|
+
.filter(capability => tools.some(tool => tool.name === capability.toolName)).map(capability => structuredClone(capability)) }),
|
|
17
|
+
...(source.verificationProjection ? {verificationProjection:source.verificationProjection} : {}) };
|
|
18
|
+
validateModelSurface(result); return result;
|
|
19
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { canonical } from '@kingsoftcloud/agentkit-harness-core';
|
|
2
|
+
|
|
3
|
+
const key = value => canonical({ scope: value.scope, runId: value.runId });
|
|
4
|
+
const clip = (value, limit = 16000) => {
|
|
5
|
+
const text = typeof value === 'string' ? value : canonical(value ?? null);
|
|
6
|
+
return { text: text.slice(0, limit), omittedChars: Math.max(0, text.length - limit) };
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/** Latest observed read/search activity, without results or execution authority. */
|
|
10
|
+
export function nativeChildActivity(record) {
|
|
11
|
+
const names = new Set(['read_workspace_file', 'list_workspace_files', 'search_workspace_files']);
|
|
12
|
+
const intent = (record.intents ?? []).findLast(row => row.kind === 'tool' && row.payload.calls?.some(call => names.has(call.name)));
|
|
13
|
+
const calls = intent?.payload.calls ?? [], index = calls.findLastIndex(call => names.has(call.name)), call = calls[index];
|
|
14
|
+
if (!call) return null;
|
|
15
|
+
const receipt = (record.receipts ?? []).find(row => row.operationKey === intent.operationKey && row.status === 'committed');
|
|
16
|
+
const result = receipt?.result.results?.[index];
|
|
17
|
+
const running = !receipt && record.snapshot.pendingIntent === intent.operationKey && record.snapshot.phase === 'executing_tools'
|
|
18
|
+
&& record.lease?.expiresAt > Date.now();
|
|
19
|
+
return { name: call.name, input: Object.fromEntries(['path', 'directory', 'pattern', 'query'].flatMap(key =>
|
|
20
|
+
typeof call.arguments?.[key] === 'string' ? [[key, call.arguments[key].slice(0, 512)]] : [])),
|
|
21
|
+
status: { ok: 'completed', error: 'failed', uncertain: 'uncertain', approval_required: 'waiting' }[result?.status]
|
|
22
|
+
?? (running ? 'running' : 'scheduled') };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Ephemeral provider text only. It grants no execution, receipt or recovery authority. */
|
|
26
|
+
export function createNativeChildObservationCache({ maxRuns = 32, maxChars = 65536, now = Date.now } = {}) {
|
|
27
|
+
const entries = new Map();
|
|
28
|
+
return {
|
|
29
|
+
accept(context, record) {
|
|
30
|
+
try {
|
|
31
|
+
if (context.signal?.aborted || !['text', 'thinking'].includes(record.event?.kind)) return;
|
|
32
|
+
const id = key(context), previous = entries.get(id);
|
|
33
|
+
const entry = previous?.operationKey === context.operationKey && previous.fence === context.executionFence
|
|
34
|
+
? previous : { operationKey: context.operationKey, fence: context.executionFence, text: '', thinking: '', textOmitted: 0, thinkingOmitted: 0 };
|
|
35
|
+
const field = record.event.kind === 'thinking' ? 'thinking' : 'text';
|
|
36
|
+
const delta = record.event.text;
|
|
37
|
+
if (typeof delta !== 'string') return;
|
|
38
|
+
const available = Math.max(0, maxChars - entry[field].length);
|
|
39
|
+
entry[field] += delta.slice(0, available); entry[field + "Omitted"] += Math.max(0, delta.length - available); entry.time = now();
|
|
40
|
+
entries.delete(id); entries.set(id, entry);
|
|
41
|
+
while (entries.size > maxRuns) entries.delete(entries.keys().next().value);
|
|
42
|
+
} catch { /* presentation cannot interrupt model execution */ }
|
|
43
|
+
},
|
|
44
|
+
read(record) {
|
|
45
|
+
const entry = entries.get(key(record.snapshot));
|
|
46
|
+
if (!entry || entry.operationKey !== record.snapshot.pendingIntent || entry.fence !== record.fence
|
|
47
|
+
|| record.snapshot.phase !== 'requesting_model' || !record.lease || record.lease.expiresAt <= now()
|
|
48
|
+
|| record.receipts.some(receipt => receipt.operationKey === entry.operationKey)) return null;
|
|
49
|
+
return { operationKey: entry.operationKey, text: entry.text,
|
|
50
|
+
thinking: record.configuration.profile.providerThinkingDisplayPolicy === 'provider-visible/v1' ? entry.thinking : '',
|
|
51
|
+
omittedChars: entry.textOmitted + (record.configuration.profile.providerThinkingDisplayPolicy === 'provider-visible/v1' ? entry.thinkingOmitted : 0) };
|
|
52
|
+
},
|
|
53
|
+
clear() { entries.clear(); },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Bounded child-only public observations. System context and replay data never leave this view. */
|
|
58
|
+
export function nativeChildObservation(record, live = null) {
|
|
59
|
+
const { snapshot, configuration } = record;
|
|
60
|
+
const reasoning = new Map();
|
|
61
|
+
if (configuration.profile.providerThinkingDisplayPolicy === 'provider-visible/v1')
|
|
62
|
+
for (const event of record.events ?? []) if (event.kind === 'model.completed' && Number.isSafeInteger(event.payload.messageIndex)
|
|
63
|
+
&& snapshot.messages[event.payload.messageIndex]?.role === 'assistant'
|
|
64
|
+
&& snapshot.messages[event.payload.messageIndex]?.content === event.payload.text) {
|
|
65
|
+
const text = (event.payload.displayStream?.records ?? []).filter(row => row.event?.kind === 'thinking').map(row => row.event.text).join('');
|
|
66
|
+
if (text) reasoning.set(event.payload.messageIndex, clip(text));
|
|
67
|
+
}
|
|
68
|
+
const results = new Map();
|
|
69
|
+
for (const receipt of record.receipts) {
|
|
70
|
+
if (receipt.status !== 'committed') continue;
|
|
71
|
+
const intent = record.intents.find(intent => intent.operationKey === receipt.operationKey);
|
|
72
|
+
const pairs = intent?.kind === 'tool' ? (intent.payload.calls ?? []).map((call, i) => [call.callId, receipt.result.results?.[i]])
|
|
73
|
+
: ['tool_receipt', 'workspace_repair'].includes(intent?.kind) ? [[intent.payload.callId, receipt.result.result]] : [];
|
|
74
|
+
for (const [id, result] of pairs) if (result) results.set(id, result);
|
|
75
|
+
}
|
|
76
|
+
const rows = [];
|
|
77
|
+
snapshot.messages.forEach((message, index) => {
|
|
78
|
+
if (message.history || message.role === 'system' || message.kind === 'compaction_summary') return;
|
|
79
|
+
if (message.role === 'assistant') {
|
|
80
|
+
if (reasoning.has(index)) rows.push({ id: `thought:${index}`, kind: 'thought', ...reasoning.get(index) });
|
|
81
|
+
if (message.content) rows.push({ id: `text:${index}`, kind: 'text', ...clip(message.content) });
|
|
82
|
+
for (const call of message.toolCalls ?? []) rows.push({ id: call.callId, kind: 'tool', name: call.name,
|
|
83
|
+
input: clip(call.arguments), status: 'scheduled' });
|
|
84
|
+
} else if (message.role === 'tool') {
|
|
85
|
+
const call = rows.find(row => row.kind === 'tool' && row.id === message.callId);
|
|
86
|
+
if (call) {
|
|
87
|
+
const result = results.get(message.callId);
|
|
88
|
+
call.status = { ok: 'completed', error: 'failed', uncertain: 'uncertain', approval_required: 'waiting' }[result?.status] ?? 'interrupted';
|
|
89
|
+
call.result = clip(message.content);
|
|
90
|
+
if (result?.status === 'ok' && result.metadata?.nativeTaskDisplay?.contract === 'agentkit.workspace-display/v1')
|
|
91
|
+
call.display = result.metadata.nativeTaskDisplay.display;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const selected = rows.slice(-120);
|
|
96
|
+
return { contract: 'agentkit.native-child-observation/v1', runId: snapshot.runId, sessionId: snapshot.sessionId,
|
|
97
|
+
workspaceView: configuration.hostRecoveryBinding?.childWorkspace ? 'isolated' : 'parent',
|
|
98
|
+
snapshotRevision: snapshot.revision, prompt: clip(snapshot.goal), driving: Boolean(record.lease && record.lease.expiresAt > Date.now()), phase: snapshot.phase, outcome: snapshot.outcome, totalRows: rows.length,
|
|
99
|
+
omittedRows: Math.max(0, rows.length - selected.length), rows: selected, live };
|
|
100
|
+
}
|