@llblab/pi-actors 0.42.3 → 0.43.1
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/AGENTS.md +127 -175
- package/BACKLOG.md +189 -1
- package/CHANGELOG.md +183 -292
- package/README.md +115 -276
- package/dist/fixtures/protocol/control-endpoint.json +6 -0
- package/dist/fixtures/protocol/control-record.json +9 -0
- package/dist/fixtures/protocol/recipe-summary.json +4 -12
- package/dist/fixtures/protocol/trace-event.json +9 -0
- package/dist/index.js +1 -1
- package/dist/lib/async-runs.d.ts +15 -38
- package/dist/lib/async-runs.js +173 -111
- package/dist/lib/automatic-review-runtime.d.ts +1 -1
- package/dist/lib/automatic-review-runtime.js +5 -5
- package/dist/lib/command-templates.d.ts +2 -0
- package/dist/lib/command-templates.js +38 -4
- package/dist/lib/control-projection.d.ts +20 -0
- package/dist/lib/control-projection.js +66 -0
- package/dist/lib/control.d.ts +15 -0
- package/dist/lib/control.js +97 -0
- package/dist/lib/draft-sleep.js +3 -3
- package/dist/lib/execution-sessions.d.ts +17 -0
- package/dist/lib/execution-sessions.js +85 -0
- package/dist/lib/file-state.d.ts +2 -0
- package/dist/lib/file-state.js +114 -46
- package/dist/lib/inspector-actions.d.ts +2 -2
- package/dist/lib/inspector-actions.js +2 -2
- package/dist/lib/inspector-command.js +3 -3
- package/dist/lib/inspector-overlay.d.ts +54 -70
- package/dist/lib/inspector-overlay.js +576 -910
- package/dist/lib/inspector.d.ts +3 -71
- package/dist/lib/inspector.js +19 -665
- package/dist/lib/limits.d.ts +7 -3
- package/dist/lib/limits.js +7 -3
- package/dist/lib/observability.d.ts +16 -16
- package/dist/lib/observability.js +43 -82
- package/dist/lib/prompts.d.ts +1 -1
- package/dist/lib/prompts.js +3 -3
- package/dist/lib/recipe-control.d.ts +7 -0
- package/dist/lib/recipe-control.js +43 -0
- package/dist/lib/recipes-discovery.js +2 -0
- package/dist/lib/recipes-references.d.ts +1 -14
- package/dist/lib/recipes-references.js +6 -21
- package/dist/lib/review-control.d.ts +1 -1
- package/dist/lib/review-control.js +4 -5
- package/dist/lib/review-projection.js +1 -5
- package/dist/lib/run-ui-runtime.js +2 -2
- package/dist/lib/runs-control-delivery.d.ts +28 -0
- package/dist/lib/runs-control-delivery.js +150 -0
- package/dist/lib/runs-controls.d.ts +37 -0
- package/dist/lib/runs-controls.js +146 -0
- package/dist/lib/runs-retention.d.ts +7 -0
- package/dist/lib/runs-retention.js +27 -3
- package/dist/lib/runs-start.js +4 -2
- package/dist/lib/runs-status.js +11 -6
- package/dist/lib/runs-trace.d.ts +24 -0
- package/dist/lib/runs-trace.js +102 -0
- package/dist/lib/runtime-identity.d.ts +7 -0
- package/dist/lib/runtime-identity.js +35 -0
- package/dist/lib/runtime-notifier.d.ts +1 -1
- package/dist/lib/runtime-notifier.js +1 -1
- package/dist/lib/runtime-triage.d.ts +29 -0
- package/dist/lib/runtime-triage.js +76 -0
- package/dist/lib/tool-review-scheduler.js +7 -7
- package/dist/lib/tools-inspect.d.ts +3 -3
- package/dist/lib/tools-inspect.js +241 -707
- package/dist/lib/tools-local.js +2 -10
- package/dist/lib/tools-message.d.ts +6 -7
- package/dist/lib/tools-message.js +95 -396
- package/dist/lib/tools-response.d.ts +1 -5
- package/dist/lib/tools-response.js +5 -48
- package/dist/lib/tools-spawn.js +16 -28
- package/dist/lib/tools.d.ts +1 -1
- package/dist/lib/tools.js +2 -3
- package/dist/lib/trace-projection.d.ts +22 -0
- package/dist/lib/trace-projection.js +185 -0
- package/dist/recipes/draft-review.json +0 -10
- package/dist/recipes/lens-swarm.json +0 -14
- package/dist/recipes/music-player.json +10 -19
- package/dist/recipes/pipeline-architect-coordinator.json +0 -11
- package/dist/recipes/pipeline-artifact-bundle.json +1 -22
- package/dist/recipes/pipeline-artifact-report.json +1 -18
- package/dist/recipes/pipeline-artifact-write.json +1 -18
- package/dist/recipes/pipeline-async-run-ops.json +0 -12
- package/dist/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/dist/recipes/pipeline-development-tasking.json +0 -12
- package/dist/recipes/pipeline-docs-maintenance.json +0 -12
- package/dist/recipes/pipeline-media-library.json +0 -12
- package/dist/recipes/pipeline-quorum-review.json +0 -12
- package/dist/recipes/pipeline-release-readiness.json +0 -12
- package/dist/recipes/pipeline-release-summary.json +0 -12
- package/dist/recipes/pipeline-repo-health.json +0 -12
- package/dist/recipes/pipeline-research-synthesis.json +0 -11
- package/dist/recipes/pipeline-review-readiness.json +0 -12
- package/dist/recipes/resource-locker.json +27 -0
- package/dist/recipes/subagent-artifact.json +0 -9
- package/dist/recipes/subagent-checkpoint.json +0 -10
- package/dist/recipes/subagent-conflict-report.json +0 -11
- package/dist/recipes/subagent-contradiction-map.json +0 -11
- package/dist/recipes/subagent-critic.json +0 -11
- package/dist/recipes/subagent-evidence-map.json +0 -11
- package/dist/recipes/subagent-followup.json +0 -10
- package/dist/recipes/subagent-judge.json +0 -11
- package/dist/recipes/subagent-merge.json +0 -11
- package/dist/recipes/subagent-normalize.json +0 -11
- package/dist/recipes/subagent-plan.json +0 -11
- package/dist/recipes/subagent-preflight.json +0 -11
- package/dist/recipes/subagent-prompt.json +0 -10
- package/dist/recipes/subagent-quorum.json +0 -10
- package/dist/recipes/subagent-review-coordinator.json +0 -14
- package/dist/recipes/subagent-review.json +0 -11
- package/dist/recipes/subagent-task-card.json +0 -11
- package/dist/recipes/subagent-tools.json +0 -10
- package/dist/recipes/subagent-verify.json +0 -11
- package/dist/recipes/subagents-prompts.json +0 -10
- package/dist/recipes/tool-review.json +0 -10
- package/dist/scripts/async-runner.mjs +25 -25
- package/dist/scripts/conformance.mjs +4 -2
- package/dist/scripts/locker.mjs +196 -69
- package/dist/scripts/music-player.mjs +162 -159
- package/dist/scripts/recipe-utils.mjs +6 -96
- package/dist/scripts/release-gates.mjs +91 -0
- package/dist/scripts/validate-recipe.mjs +8 -57
- package/dist/skills/actors/SKILL.md +57 -265
- package/dist/skills/swarm/SKILL.md +10 -34
- package/docs/0.43-baseline.md +39 -0
- package/docs/README.md +4 -6
- package/docs/actor-inspector.md +26 -64
- package/docs/async-runs.md +81 -328
- package/docs/command-templates.md +8 -118
- package/docs/recipe-library.md +55 -182
- package/docs/releasing.md +28 -0
- package/docs/template-recipes.md +76 -289
- package/docs/tool-registry.md +41 -161
- package/fixtures/protocol/control-endpoint.json +6 -0
- package/fixtures/protocol/control-record.json +9 -0
- package/fixtures/protocol/recipe-summary.json +4 -12
- package/fixtures/protocol/trace-event.json +9 -0
- package/index.ts +1 -1
- package/lib/async-runs.ts +218 -204
- package/lib/automatic-review-runtime.ts +7 -7
- package/lib/command-templates.ts +44 -4
- package/lib/control-projection.ts +105 -0
- package/lib/control.ts +117 -0
- package/lib/draft-sleep.ts +3 -3
- package/lib/execution-sessions.ts +111 -0
- package/lib/file-state.ts +84 -64
- package/lib/inspector-actions.ts +2 -2
- package/lib/inspector-command.ts +3 -3
- package/lib/inspector-overlay.ts +617 -1126
- package/lib/inspector.ts +46 -979
- package/lib/limits.ts +7 -3
- package/lib/observability.ts +60 -101
- package/lib/prompts.ts +3 -3
- package/lib/recipe-control.ts +52 -0
- package/lib/recipes-discovery.ts +2 -0
- package/lib/recipes-references.ts +9 -45
- package/lib/review-control.ts +4 -5
- package/lib/review-projection.ts +1 -5
- package/lib/run-ui-runtime.ts +2 -2
- package/lib/runs-control-delivery.ts +209 -0
- package/lib/runs-controls.ts +213 -0
- package/lib/runs-retention.ts +38 -3
- package/lib/runs-start.ts +4 -2
- package/lib/runs-status.ts +11 -6
- package/lib/runs-trace.ts +136 -0
- package/lib/runtime-identity.ts +39 -0
- package/lib/runtime-notifier.ts +1 -1
- package/lib/runtime-triage.ts +120 -0
- package/lib/tool-review-scheduler.ts +7 -7
- package/lib/tools-inspect.ts +283 -900
- package/lib/tools-local.ts +2 -12
- package/lib/tools-message.ts +112 -520
- package/lib/tools-response.ts +5 -64
- package/lib/tools-spawn.ts +16 -32
- package/lib/tools.ts +5 -6
- package/lib/trace-projection.ts +244 -0
- package/package.json +2 -1
- package/recipes/draft-review.json +0 -10
- package/recipes/lens-swarm.json +0 -14
- package/recipes/music-player.json +10 -19
- package/recipes/pipeline-architect-coordinator.json +0 -11
- package/recipes/pipeline-artifact-bundle.json +1 -22
- package/recipes/pipeline-artifact-report.json +1 -18
- package/recipes/pipeline-artifact-write.json +1 -18
- package/recipes/pipeline-async-run-ops.json +0 -12
- package/recipes/pipeline-checkpoint-continuation.json +0 -14
- package/recipes/pipeline-development-tasking.json +0 -12
- package/recipes/pipeline-docs-maintenance.json +0 -12
- package/recipes/pipeline-media-library.json +0 -12
- package/recipes/pipeline-quorum-review.json +0 -12
- package/recipes/pipeline-release-readiness.json +0 -12
- package/recipes/pipeline-release-summary.json +0 -12
- package/recipes/pipeline-repo-health.json +0 -12
- package/recipes/pipeline-research-synthesis.json +0 -11
- package/recipes/pipeline-review-readiness.json +0 -12
- package/recipes/resource-locker.json +27 -0
- package/recipes/subagent-artifact.json +0 -9
- package/recipes/subagent-checkpoint.json +0 -10
- package/recipes/subagent-conflict-report.json +0 -11
- package/recipes/subagent-contradiction-map.json +0 -11
- package/recipes/subagent-critic.json +0 -11
- package/recipes/subagent-evidence-map.json +0 -11
- package/recipes/subagent-followup.json +0 -10
- package/recipes/subagent-judge.json +0 -11
- package/recipes/subagent-merge.json +0 -11
- package/recipes/subagent-normalize.json +0 -11
- package/recipes/subagent-plan.json +0 -11
- package/recipes/subagent-preflight.json +0 -11
- package/recipes/subagent-prompt.json +0 -10
- package/recipes/subagent-quorum.json +0 -10
- package/recipes/subagent-review-coordinator.json +0 -14
- package/recipes/subagent-review.json +0 -11
- package/recipes/subagent-task-card.json +0 -11
- package/recipes/subagent-tools.json +0 -10
- package/recipes/subagent-verify.json +0 -11
- package/recipes/subagents-prompts.json +0 -10
- package/recipes/tool-review.json +0 -10
- package/scripts/async-runner.mjs +25 -25
- package/scripts/conformance.mjs +4 -2
- package/scripts/locker.mjs +196 -69
- package/scripts/music-player.mjs +162 -159
- package/scripts/recipe-utils.mjs +6 -96
- package/scripts/release-gates.mjs +91 -0
- package/scripts/validate-recipe.mjs +8 -57
- package/skills/actors/SKILL.md +57 -265
- package/skills/swarm/SKILL.md +10 -34
- package/dist/fixtures/protocol/actor-message-branch.json +0 -13
- package/dist/fixtures/protocol/mailbox-contract.json +0 -15
- package/dist/fixtures/protocol/room-message.json +0 -11
- package/dist/fixtures/protocol/room-roster.json +0 -11
- package/dist/fixtures/protocol/run-inbox-message.json +0 -9
- package/dist/fixtures/protocol/run-outbox-event.json +0 -9
- package/dist/lib/mailbox-loop.d.ts +0 -41
- package/dist/lib/mailbox-loop.js +0 -60
- package/dist/lib/messages.d.ts +0 -25
- package/dist/lib/messages.js +0 -122
- package/dist/lib/rooms.d.ts +0 -104
- package/dist/lib/rooms.js +0 -647
- package/dist/lib/runs-mailbox.d.ts +0 -25
- package/dist/lib/runs-mailbox.js +0 -146
- package/dist/lib/runs-messages.d.ts +0 -15
- package/dist/lib/runs-messages.js +0 -179
- package/dist/lib/runs-outbox.d.ts +0 -41
- package/dist/lib/runs-outbox.js +0 -87
- package/dist/lib/tools-mailbox.d.ts +0 -8
- package/dist/lib/tools-mailbox.js +0 -48
- package/dist/recipes/actor-worker.json +0 -39
- package/dist/recipes/coordinator-locker.json +0 -45
- package/dist/recipes/locker.json +0 -45
- package/dist/recipes/pipeline-room-swarm.json +0 -50
- package/dist/recipes/subagent-message.json +0 -32
- package/dist/recipes/utility-actor-message.json +0 -23
- package/dist/scripts/actor-worker.mjs +0 -214
- package/dist/scripts/coordinator.mjs +0 -799
- package/docs/actor-messages.md +0 -225
- package/docs/actors-deep-reference.md +0 -66
- package/docs/component-recipes.md +0 -148
- package/docs/task-first-recipes.md +0 -263
- package/fixtures/protocol/actor-message-branch.json +0 -13
- package/fixtures/protocol/mailbox-contract.json +0 -15
- package/fixtures/protocol/room-message.json +0 -11
- package/fixtures/protocol/room-roster.json +0 -11
- package/fixtures/protocol/run-inbox-message.json +0 -9
- package/fixtures/protocol/run-outbox-event.json +0 -9
- package/lib/mailbox-loop.ts +0 -144
- package/lib/messages.ts +0 -151
- package/lib/rooms.ts +0 -939
- package/lib/runs-mailbox.ts +0 -208
- package/lib/runs-messages.ts +0 -252
- package/lib/runs-outbox.ts +0 -144
- package/lib/tools-mailbox.ts +0 -56
- package/recipes/actor-worker.json +0 -39
- package/recipes/coordinator-locker.json +0 -45
- package/recipes/locker.json +0 -45
- package/recipes/pipeline-room-swarm.json +0 -50
- package/recipes/subagent-message.json +0 -32
- package/recipes/utility-actor-message.json +0 -23
- package/scripts/actor-worker.mjs +0 -214
- package/scripts/coordinator.mjs +0 -799
package/lib/tools-message.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Public message tool
|
|
3
|
-
* Zones:
|
|
4
|
-
* Owns
|
|
2
|
+
* Public Control facade under the stable `message` tool name.
|
|
3
|
+
* Zones: Control request validation, Run/runtime dispatch, compact receipts
|
|
4
|
+
* Owns public Control execution; journaling, delivery, and lifecycle mutation stay in Run domains.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import * as AsyncRuns from "./async-runs.ts";
|
|
8
|
-
import * as
|
|
9
|
-
import * as Rooms from "./rooms.ts";
|
|
8
|
+
import * as Control from "./control.ts";
|
|
10
9
|
import * as Schema from "./schema.ts";
|
|
11
10
|
import * as ToolsAccess from "./tools-access.ts";
|
|
12
|
-
import * as ToolsMailbox from "./tools-mailbox.ts";
|
|
13
11
|
import * as ToolsResponse from "./tools-response.ts";
|
|
14
12
|
|
|
15
13
|
function asRecord(value: unknown): Record<string, unknown> {
|
|
@@ -18,325 +16,62 @@ function asRecord(value: unknown): Record<string, unknown> {
|
|
|
18
16
|
: {};
|
|
19
17
|
}
|
|
20
18
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
verbose: boolean | undefined,
|
|
24
|
-
compact: string,
|
|
25
|
-
): string {
|
|
26
|
-
return verbose ? ToolsResponse.jsonText(value) : compact;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function messageBodyToRunLine(message: Messages.ActorMessage): string {
|
|
30
|
-
if (message.type !== "run.message") return JSON.stringify(message);
|
|
31
|
-
if (typeof message.body === "string") return message.body;
|
|
32
|
-
if (message.body === undefined) return message.type;
|
|
33
|
-
return JSON.stringify(message.body);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function messageBodyToToolParams(
|
|
37
|
-
message: Messages.ActorMessage,
|
|
38
|
-
): Record<string, unknown> {
|
|
39
|
-
if (
|
|
40
|
-
message.body &&
|
|
41
|
-
typeof message.body === "object" &&
|
|
42
|
-
!Array.isArray(message.body)
|
|
43
|
-
) {
|
|
44
|
-
return message.body as Record<string, unknown>;
|
|
45
|
-
}
|
|
46
|
-
if (message.body === undefined) return {};
|
|
47
|
-
return { input: message.body };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function formatToolActorFailure(
|
|
51
|
-
tool: string,
|
|
52
|
-
message: Messages.ActorMessage,
|
|
53
|
-
params: Record<string, unknown>,
|
|
54
|
-
error: unknown,
|
|
55
|
-
): Error {
|
|
56
|
-
const original = error instanceof Error ? error.message : String(error);
|
|
57
|
-
const paramsPreview = ToolsResponse.compactPreview(params, 240) ?? "{}";
|
|
58
|
-
return Object.assign(
|
|
59
|
-
new Error(
|
|
60
|
-
`tool actor ${tool} failed for message type ${message.type}: ${original}; params=${paramsPreview}`,
|
|
61
|
-
),
|
|
62
|
-
{
|
|
63
|
-
message_type: message.type,
|
|
64
|
-
original_error: original,
|
|
65
|
-
params_preview: paramsPreview,
|
|
66
|
-
tool,
|
|
67
|
-
},
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function assertMessageSenderBelongsToRun(
|
|
72
|
-
message: Messages.ActorMessage,
|
|
73
|
-
run: string,
|
|
74
|
-
routeLabel: string,
|
|
75
|
-
): void {
|
|
76
|
-
if (!message.from) {
|
|
77
|
-
throw new Error(`message to ${message.to} requires from=<actor address>.`);
|
|
78
|
-
}
|
|
79
|
-
const sender = Messages.parseActorAddress(message.from);
|
|
80
|
-
if (
|
|
81
|
-
(sender.kind !== "run" && sender.kind !== "branch") ||
|
|
82
|
-
sender.value !== run
|
|
83
|
-
) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`message to ${routeLabel} requires from=run:${run} or branch:${run}/<branch>; got ${message.from}.`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function routeBranchEnvelope(
|
|
91
|
-
stateDir: string,
|
|
92
|
-
runId: string,
|
|
93
|
-
recipient: string,
|
|
94
|
-
message: Messages.ActorMessage,
|
|
95
|
-
_options: { source: "direct" | "room-multicast" },
|
|
96
|
-
): Promise<Record<string, unknown>> {
|
|
97
|
-
const branchMessage = { ...message, to: recipient };
|
|
98
|
-
Rooms.appendBranchInboxMessage(stateDir, runId, recipient, branchMessage);
|
|
99
|
-
try {
|
|
100
|
-
return await AsyncRuns.sendRunMessage(runId, JSON.stringify(branchMessage));
|
|
101
|
-
} catch (error) {
|
|
102
|
-
const record =
|
|
103
|
-
error && typeof error === "object"
|
|
104
|
-
? (error as Record<string, unknown>)
|
|
105
|
-
: {};
|
|
106
|
-
if (record.queued === true) {
|
|
107
|
-
return {
|
|
108
|
-
control_path: record.control_path,
|
|
109
|
-
control_type: record.control_type,
|
|
110
|
-
delivery_error:
|
|
111
|
-
record.delivery_error ??
|
|
112
|
-
(error instanceof Error ? error.message : String(error)),
|
|
113
|
-
inbox_id: record.inbox_id,
|
|
114
|
-
queued: true,
|
|
115
|
-
run: runId,
|
|
116
|
-
sent: false,
|
|
117
|
-
state_dir: stateDir,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
throw error;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function getRoomMulticastRecipients(
|
|
125
|
-
message: Messages.ActorMessage,
|
|
126
|
-
run: string,
|
|
127
|
-
): string[] {
|
|
128
|
-
const raw = message.metadata?.recipients;
|
|
129
|
-
if (raw === undefined) return [];
|
|
130
|
-
if (!Array.isArray(raw)) {
|
|
131
|
-
throw new Error("room multicast metadata.recipients must be an array.");
|
|
132
|
-
}
|
|
133
|
-
return raw.map((recipient) => {
|
|
134
|
-
if (typeof recipient !== "string") {
|
|
135
|
-
throw new Error("room multicast recipients must be actor addresses.");
|
|
136
|
-
}
|
|
137
|
-
const parsed = Messages.parseActorAddress(recipient);
|
|
138
|
-
if (parsed.kind !== "branch" || parsed.value !== run) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`room multicast recipient must be branch:${run}/<branch>; got ${recipient}.`,
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
return Messages.formatActorAddress(parsed);
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function normalizeDeliveryOutcome(
|
|
148
|
-
address: Messages.ActorAddress,
|
|
149
|
-
result: Record<string, unknown>,
|
|
150
|
-
): Record<string, unknown> {
|
|
151
|
-
if (result.reason) return result;
|
|
152
|
-
if (address.kind === "run") {
|
|
153
|
-
if (result.stopped === true) {
|
|
154
|
-
return {
|
|
155
|
-
...result,
|
|
156
|
-
consumer: "run-control",
|
|
157
|
-
delivered: true,
|
|
158
|
-
persisted: true,
|
|
159
|
-
reason: "control_applied",
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
...result,
|
|
164
|
-
consumer: result.control_type ?? result.control ?? "run-control",
|
|
165
|
-
delivered: result.sent === true && result.queued !== true,
|
|
166
|
-
persisted: true,
|
|
167
|
-
reason: result.delivery_error
|
|
168
|
-
? "delivery_failed_persisted"
|
|
169
|
-
: result.queued === true
|
|
170
|
-
? "queued_mailbox"
|
|
171
|
-
: "delivered",
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
if (address.kind === "branch") {
|
|
175
|
-
return {
|
|
176
|
-
...result,
|
|
177
|
-
consumer: "branch-mailbox",
|
|
178
|
-
delivered: result.sent === true,
|
|
179
|
-
persisted: true,
|
|
180
|
-
reason: result.delivery_error
|
|
181
|
-
? "branch_persisted_parent_unavailable"
|
|
182
|
-
: "branch_persisted_forwarded",
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
if (address.kind === "room") {
|
|
186
|
-
return {
|
|
187
|
-
...result,
|
|
188
|
-
consumer: "room-timeline",
|
|
189
|
-
delivered: true,
|
|
190
|
-
forwarded: Number(result.multicast_count ?? 0) > 0,
|
|
191
|
-
persisted: true,
|
|
192
|
-
reason: "room_persisted",
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
if (address.kind === "tool") {
|
|
196
|
-
return {
|
|
197
|
-
...result,
|
|
198
|
-
consumer: "tool",
|
|
199
|
-
delivered: true,
|
|
200
|
-
persisted: false,
|
|
201
|
-
reason: "tool_invoked",
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
if (address.kind === "coordinator" || address.kind === "session") {
|
|
205
|
-
return {
|
|
206
|
-
...result,
|
|
207
|
-
consumer: "run-outbox",
|
|
208
|
-
delivered: false,
|
|
209
|
-
persisted: true,
|
|
210
|
-
queued: true,
|
|
211
|
-
reason: `${address.kind}_outbox_persisted`,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
return result;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function actorMessageNextActions(
|
|
218
|
-
message: Messages.ActorMessage,
|
|
219
|
-
result: Record<string, unknown>,
|
|
220
|
-
): string[] {
|
|
221
|
-
const actions: string[] = [];
|
|
222
|
-
const address = Messages.parseActorAddress(message.to);
|
|
223
|
-
if (result.delivery_error || result.sent === false) {
|
|
224
|
-
if (address.kind === "run" && address.value) {
|
|
225
|
-
actions.push(`inspect target=run:${address.value} view=status`);
|
|
226
|
-
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
227
|
-
} else if (address.kind === "branch" && address.value) {
|
|
228
|
-
actions.push(
|
|
229
|
-
`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`,
|
|
230
|
-
);
|
|
231
|
-
actions.push(`inspect target=run:${address.value} view=status`);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
if (result.queued === true) {
|
|
235
|
-
if (address.kind === "branch" && address.value) {
|
|
236
|
-
actions.push(
|
|
237
|
-
`inspect target=branch:${address.value}/${address.branch ?? "main"} view=mailbox`,
|
|
238
|
-
);
|
|
239
|
-
} else if (address.kind === "run" && address.value) {
|
|
240
|
-
actions.push(`inspect target=run:${address.value} view=mailbox`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return [...new Set(actions)].slice(0, 3);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function compactActorMessageResult(
|
|
247
|
-
message: Messages.ActorMessage,
|
|
19
|
+
function compactControlResult(
|
|
20
|
+
request: Control.ControlRequest,
|
|
248
21
|
result: Record<string, unknown>,
|
|
249
22
|
): string {
|
|
250
23
|
const tokens = [
|
|
251
|
-
`
|
|
252
|
-
`
|
|
253
|
-
`
|
|
24
|
+
`target=${request.target}`,
|
|
25
|
+
`action=${request.action}`,
|
|
26
|
+
`status=${String(result.delivery ?? result.status ?? (result.stopped === true ? "handled" : "accepted"))}`,
|
|
254
27
|
];
|
|
255
|
-
if (result.
|
|
256
|
-
if (result.
|
|
257
|
-
|
|
258
|
-
if (result.queued === true) tokens.push("queued=true");
|
|
259
|
-
if (result.persisted !== undefined)
|
|
260
|
-
tokens.push(`persisted=${String(result.persisted)}`);
|
|
261
|
-
if (result.forwarded !== undefined)
|
|
262
|
-
tokens.push(`forwarded=${String(result.forwarded)}`);
|
|
263
|
-
if (result.consumer) tokens.push(`consumer=${String(result.consumer)}`);
|
|
264
|
-
if (result.reason) tokens.push(`reason=${String(result.reason)}`);
|
|
265
|
-
if (result.control) tokens.push(`control=${String(result.control)}`);
|
|
266
|
-
if (result.outbox) tokens.push(`messages=${String(result.outbox)}`);
|
|
267
|
-
if (result.message_count !== undefined)
|
|
268
|
-
tokens.push(`messages=${String(result.message_count)}`);
|
|
269
|
-
if (result.roster_count !== undefined)
|
|
270
|
-
tokens.push(`roster=${String(result.roster_count)}`);
|
|
271
|
-
if (result.room) tokens.push(`room=${String(result.room)}`);
|
|
272
|
-
if (result.tool) tokens.push(`tool=${String(result.tool)}`);
|
|
273
|
-
if (result.stopped === true) tokens.push("stopped=true");
|
|
274
|
-
if (result.signal) tokens.push(`signal=${String(result.signal)}`);
|
|
275
|
-
if (result.invoked === true) tokens.push("invoked=true");
|
|
276
|
-
if (result.delivery_error) {
|
|
28
|
+
if (result.control_id) tokens.push(`control=${String(result.control_id)}`);
|
|
29
|
+
if (result.reason) tokens.push(`reason=${String(result.reason).replaceAll(/\s+/g, "_")}`);
|
|
30
|
+
if (Array.isArray(result.next_actions) && result.next_actions.length > 0) {
|
|
277
31
|
tokens.push(
|
|
278
|
-
`
|
|
32
|
+
`next=${(result.next_actions as string[])
|
|
33
|
+
.map((action) => action.replaceAll(/\s+/g, "_"))
|
|
34
|
+
.join("|")}`,
|
|
279
35
|
);
|
|
280
36
|
}
|
|
281
|
-
const nextActions = Array.isArray(result.next_actions)
|
|
282
|
-
? (result.next_actions as string[])
|
|
283
|
-
: actorMessageNextActions(message, result);
|
|
284
|
-
if (nextActions.length > 0)
|
|
285
|
-
tokens.push(
|
|
286
|
-
`next=${nextActions.map((action) => action.replaceAll(/\s+/g, "_")).join("|")}`,
|
|
287
|
-
);
|
|
288
37
|
return `\n${tokens.join(" ")}`;
|
|
289
38
|
}
|
|
290
39
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
40
|
+
function runNextActions(run: string, result: Record<string, unknown>): string[] {
|
|
41
|
+
if (result.delivery === "queued" || result.delivery === "delivered") {
|
|
42
|
+
return [`inspect target=run:${run} view=control`];
|
|
43
|
+
}
|
|
44
|
+
if (result.stopped === true) return [`inspect target=run:${run} view=control`];
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ControlToolDeps {
|
|
49
|
+
handleRuntimeControl?: (
|
|
50
|
+
action: string,
|
|
51
|
+
input: unknown,
|
|
296
52
|
) => Record<string, unknown>;
|
|
297
53
|
}
|
|
298
54
|
|
|
299
|
-
export function
|
|
300
|
-
deps:
|
|
55
|
+
export function createControlToolDefinition<TContext = unknown>(
|
|
56
|
+
deps: ControlToolDeps = {},
|
|
301
57
|
): any {
|
|
302
58
|
return {
|
|
303
59
|
name: "message",
|
|
304
|
-
label: "
|
|
60
|
+
label: "Control",
|
|
305
61
|
description:
|
|
306
|
-
"
|
|
62
|
+
"Apply one Control action to an existing Run or the pi-actors runtime.",
|
|
307
63
|
parameters: Schema.objectSchema(
|
|
308
64
|
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
),
|
|
313
|
-
Schema.looseObjectSchema("
|
|
314
|
-
Schema.arraySchema("
|
|
65
|
+
target: Schema.stringSchema("Run target run:<id>, or literal runtime."),
|
|
66
|
+
action: Schema.stringSchema("Lowercase ASCII Control action, at most 64 characters."),
|
|
67
|
+
input: Schema.unionSchema([
|
|
68
|
+
Schema.stringSchema("Optional Control input string; serialized JSON must fit 380 bytes."),
|
|
69
|
+
Schema.looseObjectSchema("Optional structured Control input; serialized JSON must fit 380 bytes."),
|
|
70
|
+
Schema.arraySchema("Optional structured Control input array; serialized JSON must fit 380 bytes."),
|
|
315
71
|
]),
|
|
316
|
-
|
|
317
|
-
"Optional correlation id for workflow/task linkage.",
|
|
318
|
-
),
|
|
319
|
-
from: Schema.stringSchema(
|
|
320
|
-
"Optional sender address, such as coordinator or run:<id>.",
|
|
321
|
-
),
|
|
322
|
-
metadata: Schema.looseObjectSchema(
|
|
323
|
-
"Optional structured metadata for routing or domain hints.",
|
|
324
|
-
),
|
|
325
|
-
reply_to: Schema.stringSchema(
|
|
326
|
-
"Optional message id this message replies to.",
|
|
327
|
-
),
|
|
328
|
-
summary: Schema.stringSchema("Optional short human-facing summary."),
|
|
329
|
-
to: Schema.stringSchema(
|
|
330
|
-
"Destination actor address, e.g. run:<id> or tool:<name>; advanced: branch:<run>/<branch>, room:<run>, coordinator, session:<id>.",
|
|
331
|
-
),
|
|
332
|
-
type: Schema.stringSchema(
|
|
333
|
-
"Semantic message type, e.g. control.approve or checkpoint.needs_scope.",
|
|
334
|
-
),
|
|
335
|
-
verbose: Schema.booleanSchema(
|
|
336
|
-
"Return full JSON instead of compact text.",
|
|
337
|
-
),
|
|
72
|
+
verbose: Schema.booleanSchema("Return full JSON instead of compact text."),
|
|
338
73
|
},
|
|
339
|
-
["
|
|
74
|
+
["target", "action"],
|
|
340
75
|
),
|
|
341
76
|
async execute(
|
|
342
77
|
_toolCallId: string,
|
|
@@ -345,239 +80,96 @@ export function createActorMessageToolDefinition<TContext = unknown>(
|
|
|
345
80
|
_onUpdate: unknown,
|
|
346
81
|
ctx: TContext,
|
|
347
82
|
) {
|
|
348
|
-
const
|
|
349
|
-
const message = Messages.normalizeActorMessage(input);
|
|
350
|
-
const address = Messages.parseActorAddress(message.to);
|
|
83
|
+
const request = Control.normalizeControlRequest(params);
|
|
351
84
|
let result: Record<string, unknown>;
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
);
|
|
357
|
-
const normalizedMailbox = ToolsMailbox.normalizeMailboxContracts(
|
|
358
|
-
asRecord(status.mailbox),
|
|
359
|
-
);
|
|
360
|
-
const acceptedTypes = new Set(
|
|
361
|
-
ToolsMailbox.mailboxTypes(normalizedMailbox.accepts),
|
|
362
|
-
);
|
|
363
|
-
const advisoryWarnings =
|
|
364
|
-
acceptedTypes.size > 0 && !acceptedTypes.has(message.type)
|
|
365
|
-
? [
|
|
366
|
-
`Message type ${message.type} is not declared in mailbox.accepts for run:${address.value}.`,
|
|
367
|
-
]
|
|
368
|
-
: [];
|
|
369
|
-
if (message.type === "control.kill") {
|
|
370
|
-
result = typeof status.run_instance_id === "string"
|
|
371
|
-
? AsyncRuns.killRun(address.value, {
|
|
372
|
-
...(typeof status.ownerId === "string"
|
|
373
|
-
? { ownerId: status.ownerId }
|
|
374
|
-
: {}),
|
|
375
|
-
runInstanceId: status.run_instance_id,
|
|
376
|
-
})
|
|
377
|
-
: { killed: false, reason: "run generation unavailable" };
|
|
378
|
-
} else if (message.type === "control.archive") {
|
|
379
|
-
result = AsyncRuns.archiveRun(address.value);
|
|
380
|
-
} else if (message.type === "control.prune") {
|
|
381
|
-
const body = asRecord(message.body);
|
|
382
|
-
result = AsyncRuns.pruneRun(address.value, {
|
|
383
|
-
preserveArtifacts:
|
|
384
|
-
body.preserve_artifacts === true ||
|
|
385
|
-
body.preserveArtifacts === true,
|
|
386
|
-
});
|
|
387
|
-
} else {
|
|
388
|
-
result = await AsyncRuns.sendRunMessage(
|
|
389
|
-
address.value,
|
|
390
|
-
messageBodyToRunLine(message),
|
|
85
|
+
if (request.target === "runtime") {
|
|
86
|
+
if (request.action !== "review.retry" && request.action !== "review.reset") {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`unsupported runtime Control action: ${request.action}; use review.retry or review.reset`,
|
|
391
89
|
);
|
|
392
90
|
}
|
|
393
|
-
if (
|
|
394
|
-
|
|
395
|
-
} else if (address.kind === "branch" && address.value) {
|
|
396
|
-
const runId = address.value;
|
|
397
|
-
if (message.from)
|
|
398
|
-
assertMessageSenderBelongsToRun(
|
|
399
|
-
message,
|
|
400
|
-
runId,
|
|
401
|
-
`branch:${runId}/<branch>`,
|
|
402
|
-
);
|
|
403
|
-
const status = ToolsAccess.assertRunAccessibleToContext(runId, ctx);
|
|
404
|
-
const stateDir = String(status.state_dir ?? "");
|
|
405
|
-
if (stateDir && address.branch) {
|
|
406
|
-
const ensureBranchMember = (actorAddress: string) => {
|
|
407
|
-
Rooms.ensureRoomMember(
|
|
408
|
-
stateDir,
|
|
409
|
-
runId,
|
|
410
|
-
"main",
|
|
411
|
-
actorAddress,
|
|
412
|
-
{
|
|
413
|
-
parent: `run:${runId}`,
|
|
414
|
-
role: "branch",
|
|
415
|
-
status: "present",
|
|
416
|
-
},
|
|
417
|
-
"Branch joined default room",
|
|
418
|
-
);
|
|
419
|
-
Rooms.writeBranchCommunicationSnapshot(
|
|
420
|
-
stateDir,
|
|
421
|
-
runId,
|
|
422
|
-
actorAddress,
|
|
423
|
-
);
|
|
424
|
-
};
|
|
425
|
-
ensureBranchMember(message.to);
|
|
426
|
-
if (message.from) {
|
|
427
|
-
const sender = Messages.parseActorAddress(message.from);
|
|
428
|
-
if (sender.kind === "branch" && sender.value === runId) {
|
|
429
|
-
ensureBranchMember(message.from);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
Rooms.writeCommunicationSnapshot(stateDir, runId);
|
|
91
|
+
if (!deps.handleRuntimeControl) {
|
|
92
|
+
throw new Error("runtime Control is unavailable");
|
|
433
93
|
}
|
|
434
|
-
result =
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
: {}),
|
|
475
|
-
};
|
|
476
|
-
} else if (address.kind === "tool" && address.value) {
|
|
477
|
-
if (address.value === "pi-actors" && deps.handleRuntimeMessage) {
|
|
478
|
-
result = deps.handleRuntimeMessage(message.type, message.body);
|
|
94
|
+
result = deps.handleRuntimeControl(request.action, request.input);
|
|
95
|
+
} else {
|
|
96
|
+
const run = request.target.slice(4);
|
|
97
|
+
const status = ToolsAccess.assertRunAccessibleToContext(run, ctx);
|
|
98
|
+
const runInstanceId =
|
|
99
|
+
typeof status.run_instance_id === "string"
|
|
100
|
+
? status.run_instance_id
|
|
101
|
+
: undefined;
|
|
102
|
+
if (!runInstanceId) throw new Error(`Run generation unavailable: ${run}`);
|
|
103
|
+
if (request.action === "kill") {
|
|
104
|
+
result = AsyncRuns.killRun(run, {
|
|
105
|
+
...(typeof status.ownerId === "string"
|
|
106
|
+
? { ownerId: status.ownerId }
|
|
107
|
+
: {}),
|
|
108
|
+
runInstanceId,
|
|
109
|
+
});
|
|
110
|
+
} else if (request.action === "archive") {
|
|
111
|
+
result = AsyncRuns.archiveRun(run, {
|
|
112
|
+
...(typeof status.ownerId === "string"
|
|
113
|
+
? { ownerId: status.ownerId }
|
|
114
|
+
: {}),
|
|
115
|
+
runInstanceId,
|
|
116
|
+
});
|
|
117
|
+
} else if (request.action === "prune") {
|
|
118
|
+
const input = asRecord(request.input);
|
|
119
|
+
result = AsyncRuns.pruneRun(
|
|
120
|
+
run,
|
|
121
|
+
{
|
|
122
|
+
preserveArtifacts:
|
|
123
|
+
input.preserve_artifacts === true ||
|
|
124
|
+
input.preserveArtifacts === true,
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
...(typeof status.ownerId === "string"
|
|
128
|
+
? { ownerId: status.ownerId }
|
|
129
|
+
: {}),
|
|
130
|
+
runInstanceId,
|
|
131
|
+
},
|
|
132
|
+
);
|
|
479
133
|
} else {
|
|
480
|
-
const
|
|
481
|
-
|
|
134
|
+
const declared = Array.isArray(status.control)
|
|
135
|
+
? status.control.filter(
|
|
136
|
+
(action): action is string => typeof action === "string",
|
|
137
|
+
)
|
|
138
|
+
: [];
|
|
139
|
+
if (!declared.includes(request.action)) {
|
|
482
140
|
throw new Error(
|
|
483
|
-
`
|
|
141
|
+
`undeclared Run Control action: ${request.action}; available=${declared.join(",") || "none"}`,
|
|
484
142
|
);
|
|
485
143
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
address.value,
|
|
499
|
-
message,
|
|
500
|
-
toolParams,
|
|
501
|
-
error,
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
result = {
|
|
505
|
-
invoked: true,
|
|
506
|
-
sent: true,
|
|
507
|
-
tool: address.value,
|
|
508
|
-
tool_result: toolResult,
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
} else if (address.kind === "coordinator" || address.kind === "session") {
|
|
512
|
-
if (!message.from) {
|
|
513
|
-
throw new Error(`message to ${address.kind} requires from=run:<id>.`);
|
|
514
|
-
}
|
|
515
|
-
const sender = Messages.parseActorAddress(message.from);
|
|
516
|
-
if (sender.kind !== "run" || !sender.value) {
|
|
517
|
-
throw new Error(
|
|
518
|
-
`message to ${address.kind} currently requires from=run:<id>.`,
|
|
144
|
+
result = await AsyncRuns.sendRunControl(
|
|
145
|
+
run,
|
|
146
|
+
{
|
|
147
|
+
action: request.action,
|
|
148
|
+
...(request.input !== undefined ? { input: request.input } : {}),
|
|
149
|
+
run_instance_id: runInstanceId,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
...(typeof status.ownerId === "string"
|
|
153
|
+
? { ownerId: status.ownerId }
|
|
154
|
+
: {}),
|
|
155
|
+
},
|
|
519
156
|
);
|
|
520
157
|
}
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
ctx,
|
|
524
|
-
);
|
|
525
|
-
if (address.kind === "session") {
|
|
526
|
-
if (!senderStatus.ownerId) {
|
|
527
|
-
throw ToolsAccess.sessionMismatchError({
|
|
528
|
-
currentSession: undefined,
|
|
529
|
-
expectedSession: address.value,
|
|
530
|
-
run: sender.value,
|
|
531
|
-
target: `session:${address.value}`,
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
if (senderStatus.ownerId !== address.value) {
|
|
535
|
-
throw ToolsAccess.sessionMismatchError({
|
|
536
|
-
currentSession: String(senderStatus.ownerId),
|
|
537
|
-
expectedSession: address.value,
|
|
538
|
-
run: sender.value,
|
|
539
|
-
target: `session:${address.value}`,
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
result = AsyncRuns.appendRunOutboxEvent(sender.value, {
|
|
544
|
-
body: message.body,
|
|
545
|
-
correlation_id: message.correlation_id,
|
|
546
|
-
delivery:
|
|
547
|
-
message.metadata?.requires_response === true ||
|
|
548
|
-
address.kind === "session"
|
|
549
|
-
? "followup"
|
|
550
|
-
: undefined,
|
|
551
|
-
event: message.type,
|
|
552
|
-
from: message.from,
|
|
553
|
-
metadata: message.metadata,
|
|
554
|
-
reply_to: message.reply_to,
|
|
555
|
-
summary: message.summary,
|
|
556
|
-
to: message.to,
|
|
557
|
-
type: message.type,
|
|
558
|
-
});
|
|
559
|
-
} else {
|
|
560
|
-
throw new Error(
|
|
561
|
-
`message currently supports run:<id>, branch:<run>/<branch>, room:<run>, tool:<name>, coordinator, and session:<id> destinations; unsupported destination: ${message.to}`,
|
|
562
|
-
);
|
|
158
|
+
const nextActions = runNextActions(run, result);
|
|
159
|
+
if (nextActions.length > 0) result = { ...result, next_actions: nextActions };
|
|
563
160
|
}
|
|
564
|
-
|
|
565
|
-
const nextActions = actorMessageNextActions(message, result);
|
|
566
|
-
const resultWithNext = nextActions.length
|
|
567
|
-
? { ...result, next_actions: nextActions }
|
|
568
|
-
: result;
|
|
161
|
+
const response = { control: request, result };
|
|
569
162
|
return {
|
|
570
163
|
content: [
|
|
571
164
|
{
|
|
572
165
|
type: "text" as const,
|
|
573
|
-
text:
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
),
|
|
166
|
+
text:
|
|
167
|
+
request.verbose === true
|
|
168
|
+
? ToolsResponse.jsonText(response)
|
|
169
|
+
: compactControlResult(request, result),
|
|
578
170
|
},
|
|
579
171
|
],
|
|
580
|
-
details:
|
|
172
|
+
details: response,
|
|
581
173
|
};
|
|
582
174
|
},
|
|
583
175
|
};
|