@tea-agent/loop-agent 0.28.13 → 0.29.0
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 +1 -1
- package/CHANGELOG.md +44 -15
- package/dist/commands/client-recovery.js +56 -1
- package/dist/commands/init-upgrade.js +186 -21
- package/dist/executors/dag-pi-executor.js +49 -2
- package/dist/executors/shell-executor.js +135 -0
- package/dist/worker/console/app-data.js +132 -11
- package/dist/worker/console/chat/pi-runtime.js +24 -42
- package/dist/worker/console/chat/resource-loader.js +11 -20
- package/dist/worker/console/chat/routes.js +7 -8
- package/dist/worker/console/chat/runtime-context.js +1 -1
- package/dist/worker/console/chat/tools.js +67 -54
- package/dist/worker/console/operation-runner.js +15 -1
- package/dist/worker/console/operation-store.js +70 -49
- package/dist/worker/console/operator-actions.js +57 -1
- package/dist/worker/console/static/assets/index-Cwx-ZVEQ.js +29 -0
- package/dist/worker/console/static/favicon.svg +37 -0
- package/dist/worker/console/static/index.html +2 -1
- package/dist/workflows/dag/backend-test-scenario-param.js +846 -0
- package/dist/workflows/dag/backend-test-writer-completeness.js +418 -0
- package/dist/workflows/dag/init-hybrid.js +20 -6
- package/dist/workflows/dag/node-execution.js +31 -2
- package/dist/workflows/dag/retry-policy.js +55 -18
- package/dist/workflows/dag/types.js +1 -0
- package/dist/workflows/dag/validate.js +38 -3
- package/docs/operations/README.md +1 -1
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +3 -3
- package/docs/templates/backend-test-dag.json +36 -11
- package/docs/templates/init-managed-agents.md +1 -1
- package/harness.json +2 -2
- package/package.json +2 -1
- package/dist/worker/console/static/assets/index-BfRgtLF4.js +0 -29
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Operator Chat —
|
|
2
|
+
* Operator Chat — tool policy (ADR 0011 UX-first full Pi tools).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Operator Chat registers and activates full Pi builtins
|
|
5
|
+
* (read/write/edit/bash/grep/find/ls) plus model-callable operator actions and
|
|
6
|
+
* optional safe-* explore custom tools. Discipline is primarily soft
|
|
7
|
+
* (system prompt: prefer DAG / Human Gate). Hard denies remain for non-Pi
|
|
8
|
+
* write channels (apply_patch / full-tools / shell / coding-chat) and for
|
|
9
|
+
* modelCallable="never" actions (e.g. confirmDagConfirmation).
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* a future resource-loader or SDK drift cannot reactivate removed coding or
|
|
12
|
-
* explore tools merely by changing the active-tool list.
|
|
11
|
+
* Interview / Official surfaces keep their own OFFICIAL_DENIED path; this
|
|
12
|
+
* module only filters Chat-side denial so Interview is not widened.
|
|
13
13
|
*/
|
|
14
14
|
import { buildOperatorCapabilitiesDocument } from "../../../shared/operator/capabilities.js";
|
|
15
15
|
import { OFFICIAL_DENIED_TOOL_IDS } from "../resource-loader.js";
|
|
@@ -41,31 +41,37 @@ export const OPERATOR_CHAT_MODEL_FORBIDDEN_ACTIONS = Object.freeze(buildOperator
|
|
|
41
41
|
* stay defensive against future additions.
|
|
42
42
|
*/
|
|
43
43
|
export const OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS = Object.freeze([]);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
/** Full Pi builtin explore / coding tools admitted by ADR 0011. */
|
|
45
|
+
export const OPERATOR_CHAT_PI_BUILTIN_TOOLS = Object.freeze([
|
|
46
|
+
"read",
|
|
47
|
+
"write",
|
|
48
|
+
"edit",
|
|
49
|
+
"bash",
|
|
50
|
+
"grep",
|
|
51
|
+
"find",
|
|
52
|
+
"ls",
|
|
53
|
+
]);
|
|
54
|
+
const _PI_BUILTIN_ALLOW = new Set(OPERATOR_CHAT_PI_BUILTIN_TOOLS.map((t) => t.toLowerCase()));
|
|
50
55
|
// Operator actions that OFFICIAL_DENIED_TOOL_IDS carries for Interview safety
|
|
51
|
-
// but that the Chat now deliberately allows.
|
|
52
|
-
// (strip - and _) because OFFICIAL lists both camelCase
|
|
53
|
-
// snake_case (`contract_apply`) variants, and authorize() also normalizes —
|
|
54
|
-
// leaving any variant in would re-deny the action via normalization.
|
|
56
|
+
// but that the Chat now deliberately allows. Compare on a COMPACTED form
|
|
57
|
+
// (strip - and _) because OFFICIAL lists both camelCase and snake_case.
|
|
55
58
|
const _ALLOWED_OPERATOR_ACTIONS_COMPACT = new Set(OPERATOR_CHAT_ALLOWED_TOOLS.map((t) => t.toLowerCase().replace(/[-_]/g, "")));
|
|
59
|
+
/**
|
|
60
|
+
* Tools denied for Operator Chat. Built from OFFICIAL_DENIED minus:
|
|
61
|
+
* - Pi builtins allowed by ADR 0011
|
|
62
|
+
* - model-callable operator actions
|
|
63
|
+
* Does NOT re-add bash/read/grep/edit/write.
|
|
64
|
+
*/
|
|
56
65
|
export const OPERATOR_CHAT_DENIED_TOOLS = Object.freeze([
|
|
57
|
-
...new Set(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}),
|
|
67
|
-
..._REMOVED_SDK_EXPLORE_TOOLS,
|
|
68
|
-
]),
|
|
66
|
+
...new Set(OFFICIAL_DENIED_TOOL_IDS.filter((id) => {
|
|
67
|
+
const lower = id.toLowerCase();
|
|
68
|
+
if (_PI_BUILTIN_ALLOW.has(lower))
|
|
69
|
+
return false;
|
|
70
|
+
if (_ALLOWED_OPERATOR_ACTIONS_COMPACT.has(lower.replace(/[-_]/g, ""))) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
})),
|
|
69
75
|
]);
|
|
70
76
|
/**
|
|
71
77
|
* Operator actions denied by model policy (modelCallable="never"). These are
|
|
@@ -78,11 +84,19 @@ const DENIED_MODEL_POLICY = new Set(OPERATOR_CHAT_MODEL_FORBIDDEN_ACTIONS.map((a
|
|
|
78
84
|
const ALLOWED = new Set(OPERATOR_CHAT_ALLOWED_TOOLS.map((t) => t.toLowerCase()));
|
|
79
85
|
const DENIED_ACTIONS = new Set(OPERATOR_CHAT_DENIED_OPERATOR_ACTIONS.map((a) => a.toLowerCase()));
|
|
80
86
|
const DENIED_TOOLS = new Set(OPERATOR_CHAT_DENIED_TOOLS.map((t) => t.toLowerCase()));
|
|
87
|
+
/** Pi builtins are authorized without being operator registry actions. */
|
|
88
|
+
const PI_BUILTIN_AUTHORIZED = new Set(OPERATOR_CHAT_PI_BUILTIN_TOOLS.map((t) => t.toLowerCase()));
|
|
81
89
|
export function isOperatorChatToolAllowed(toolId) {
|
|
82
|
-
|
|
90
|
+
const id = toolId.trim().toLowerCase();
|
|
91
|
+
if (PI_BUILTIN_AUTHORIZED.has(id))
|
|
92
|
+
return true;
|
|
93
|
+
return ALLOWED.has(id);
|
|
83
94
|
}
|
|
84
95
|
export function isOperatorChatToolDenied(toolId) {
|
|
85
96
|
const id = toolId.trim().toLowerCase();
|
|
97
|
+
// Pi builtins are never denied under ADR 0011.
|
|
98
|
+
if (PI_BUILTIN_AUTHORIZED.has(id))
|
|
99
|
+
return false;
|
|
86
100
|
if (DENIED_TOOLS.has(id))
|
|
87
101
|
return true;
|
|
88
102
|
if (DENIED_ACTIONS.has(id))
|
|
@@ -125,7 +139,7 @@ export function authorizeOperatorChatTool(toolId) {
|
|
|
125
139
|
ok: false,
|
|
126
140
|
code: "tool-denied",
|
|
127
141
|
toolId: id,
|
|
128
|
-
message: `tool "${id}" is denied in General Operator Chat (no
|
|
142
|
+
message: `tool "${id}" is denied in General Operator Chat (no apply_patch/full-tools/shell/coding-chat; modelCallable=never still blocked)`,
|
|
129
143
|
};
|
|
130
144
|
}
|
|
131
145
|
if (!isOperatorChatToolAllowed(id)) {
|
|
@@ -155,35 +169,34 @@ export function filterOperatorChatTools(requested) {
|
|
|
155
169
|
return { allowed, denied };
|
|
156
170
|
}
|
|
157
171
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
172
|
+
* Non-Pi write / coding channels that must never appear in Operator Chat.
|
|
173
|
+
* ADR 0011 allows Pi builtins read/write/edit/bash/grep/find/ls; this guard
|
|
174
|
+
* only blocks alternate coding surfaces (apply_patch, full-tools, shell,
|
|
175
|
+
* coding-chat).
|
|
176
|
+
*/
|
|
177
|
+
const NON_PI_WRITE_CHANNELS = [
|
|
178
|
+
"apply_patch",
|
|
179
|
+
"apply-patch",
|
|
180
|
+
"full-tools",
|
|
181
|
+
"full_tools",
|
|
182
|
+
"coding-chat",
|
|
183
|
+
"coding_chat",
|
|
184
|
+
"shell",
|
|
185
|
+
];
|
|
186
|
+
/**
|
|
187
|
+
* Assert that a requested tool set contains no prohibited non-Pi write
|
|
188
|
+
* channel. Pi builtins (including write/edit/bash) are allowed (ADR 0011).
|
|
161
189
|
*/
|
|
162
190
|
export function assertNoWriteToolInList(requested) {
|
|
163
|
-
const writeVariants = [
|
|
164
|
-
"edit",
|
|
165
|
-
"write",
|
|
166
|
-
"apply_patch",
|
|
167
|
-
"apply-patch",
|
|
168
|
-
"full-tools",
|
|
169
|
-
"full_tools",
|
|
170
|
-
"coding-chat",
|
|
171
|
-
"coding_chat",
|
|
172
|
-
"shell",
|
|
173
|
-
"bash",
|
|
174
|
-
"read",
|
|
175
|
-
"grep",
|
|
176
|
-
];
|
|
177
191
|
const lower = new Set(requested.map((t) => t.trim().toLowerCase()));
|
|
178
|
-
for (const v of
|
|
192
|
+
for (const v of NON_PI_WRITE_CHANNELS) {
|
|
179
193
|
if (lower.has(v)) {
|
|
180
|
-
throw new Error(`
|
|
194
|
+
throw new Error(`non-Pi write channel "${v}" must not appear in Operator Chat tool list (ADR 0011)`);
|
|
181
195
|
}
|
|
182
196
|
}
|
|
183
197
|
}
|
|
184
198
|
/**
|
|
185
|
-
* Back-compat alias kept for callers that still reference the old name.
|
|
186
|
-
* also rejects removed SDK explore tools, including bash.
|
|
199
|
+
* Back-compat alias kept for callers that still reference the old name.
|
|
187
200
|
* @deprecated use {@link assertNoWriteToolInList}.
|
|
188
201
|
*/
|
|
189
202
|
export function assertNoBashInToolList(requested) {
|
|
@@ -34,6 +34,7 @@ export async function runOperation(operationId, deps) {
|
|
|
34
34
|
? deps.client.runExternal(op.externalCommand, cliArgs, options)
|
|
35
35
|
: deps.client.run(cliArgs, options));
|
|
36
36
|
let finished;
|
|
37
|
+
let spawnUpdate = Promise.resolve();
|
|
37
38
|
try {
|
|
38
39
|
const result = await run(args, {
|
|
39
40
|
cwd: deps.repoRoot,
|
|
@@ -41,10 +42,21 @@ export async function runOperation(operationId, deps) {
|
|
|
41
42
|
expectJson: true,
|
|
42
43
|
timeoutMs: deps.defaultTimeoutMs,
|
|
43
44
|
onSpawn: (info) => {
|
|
44
|
-
|
|
45
|
+
// Chain onto the per-operation write lock; never fire-and-forget a
|
|
46
|
+
// rejected update (unhandled rejection can crash Console on Windows).
|
|
47
|
+
spawnUpdate = deps.store
|
|
48
|
+
.update(operationId, {
|
|
45
49
|
state: "running",
|
|
46
50
|
pid: info.pid,
|
|
47
51
|
processStartIdentity: info.startedAt,
|
|
52
|
+
})
|
|
53
|
+
.catch((error) => {
|
|
54
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
+
deps.events.append(operationId, {
|
|
56
|
+
at: new Date().toISOString(),
|
|
57
|
+
kind: "error",
|
|
58
|
+
message: `failed to persist running state: ${message}`,
|
|
59
|
+
});
|
|
48
60
|
});
|
|
49
61
|
stateEvent(deps.events, op, "running", `pid=${info.pid ?? "?"}`);
|
|
50
62
|
},
|
|
@@ -63,9 +75,11 @@ export async function runOperation(operationId, deps) {
|
|
|
63
75
|
});
|
|
64
76
|
},
|
|
65
77
|
});
|
|
78
|
+
await spawnUpdate;
|
|
66
79
|
finished = await finalizeFromWorkerResult(operationId, result, deps);
|
|
67
80
|
}
|
|
68
81
|
catch (error) {
|
|
82
|
+
await spawnUpdate.catch(() => undefined);
|
|
69
83
|
// Cannot prove CLI exit classification → needs-reconcile (not Cancel)
|
|
70
84
|
finished = await deps.store.update(operationId, {
|
|
71
85
|
state: "needs-reconcile",
|
|
@@ -17,73 +17,94 @@ export function newOperationId() {
|
|
|
17
17
|
}
|
|
18
18
|
export class OperationStore {
|
|
19
19
|
appData;
|
|
20
|
+
/**
|
|
21
|
+
* Per-key write lock. Concurrent update/save/create on the same operation
|
|
22
|
+
* or clientRequestId must not race rename() into the same JSON path
|
|
23
|
+
* (Windows EPERM) or last-write-wins lost patches.
|
|
24
|
+
*/
|
|
25
|
+
writeChains = new Map();
|
|
20
26
|
constructor(appData) {
|
|
21
27
|
this.appData = appData;
|
|
22
28
|
}
|
|
29
|
+
serialize(key, op) {
|
|
30
|
+
const prev = this.writeChains.get(key) ?? Promise.resolve();
|
|
31
|
+
const next = prev.then(op, op);
|
|
32
|
+
this.writeChains.set(key, next.then(() => undefined, () => undefined));
|
|
33
|
+
return next;
|
|
34
|
+
}
|
|
23
35
|
async create(input) {
|
|
24
36
|
const clientRequestId = input.clientRequestId.trim();
|
|
25
37
|
if (!clientRequestId) {
|
|
26
38
|
throw new Error("clientRequestId is required for mutation operations");
|
|
27
39
|
}
|
|
28
40
|
const payloadHash = computeActionPayloadHash(input.action, input.actionParams);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (existing
|
|
35
|
-
|
|
41
|
+
return this.serialize(`req:${clientRequestId}`, async () => {
|
|
42
|
+
const indexPath = requestIndexFile(this.appData, clientRequestId);
|
|
43
|
+
const existingIndex = await readJsonIfExists(indexPath);
|
|
44
|
+
if (existingIndex) {
|
|
45
|
+
const existing = await this.get(existingIndex.operationId);
|
|
46
|
+
if (existing) {
|
|
47
|
+
if (existing.payloadHash === payloadHash) {
|
|
48
|
+
return { kind: "idempotent", operation: existing };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
kind: "conflict",
|
|
52
|
+
code: "REQUEST_ID_REUSE_CONFLICT",
|
|
53
|
+
message: `clientRequestId ${clientRequestId} was reused with a different payload`,
|
|
54
|
+
existing,
|
|
55
|
+
};
|
|
36
56
|
}
|
|
37
|
-
return {
|
|
38
|
-
kind: "conflict",
|
|
39
|
-
code: "REQUEST_ID_REUSE_CONFLICT",
|
|
40
|
-
message: `clientRequestId ${clientRequestId} was reused with a different payload`,
|
|
41
|
-
existing,
|
|
42
|
-
};
|
|
43
57
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
const operationId = newOperationId();
|
|
59
|
+
const now = new Date().toISOString();
|
|
60
|
+
const operation = {
|
|
61
|
+
schemaVersion: 1,
|
|
62
|
+
operationId,
|
|
63
|
+
clientRequestId,
|
|
64
|
+
action: input.action,
|
|
65
|
+
actionParams: input.actionParams,
|
|
66
|
+
payloadHash,
|
|
67
|
+
state: "queued",
|
|
68
|
+
createdAt: now,
|
|
69
|
+
taskId: input.taskId,
|
|
70
|
+
cliArgs: input.cliArgs,
|
|
71
|
+
...(input.externalCommand
|
|
72
|
+
? { externalCommand: input.externalCommand }
|
|
73
|
+
: {}),
|
|
74
|
+
};
|
|
75
|
+
await writeSecureJson(operationFile(this.appData, operationId), operation);
|
|
76
|
+
await writeSecureJson(indexPath, { operationId, payloadHash });
|
|
77
|
+
return { kind: "created", operation };
|
|
78
|
+
});
|
|
63
79
|
}
|
|
64
80
|
async get(operationId) {
|
|
65
81
|
return readJsonIfExists(operationFile(this.appData, operationId));
|
|
66
82
|
}
|
|
67
83
|
async save(operation) {
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
return this.serialize(operation.operationId, async () => {
|
|
85
|
+
await writeSecureJson(operationFile(this.appData, operation.operationId), operation);
|
|
86
|
+
return operation;
|
|
87
|
+
});
|
|
70
88
|
}
|
|
71
89
|
async update(operationId, patch) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
return this.serialize(operationId, async () => {
|
|
91
|
+
const current = await this.get(operationId);
|
|
92
|
+
if (!current) {
|
|
93
|
+
throw new Error(`operation not found: ${operationId}`);
|
|
94
|
+
}
|
|
95
|
+
const next = {
|
|
96
|
+
...current,
|
|
97
|
+
...patch,
|
|
98
|
+
operationId: current.operationId,
|
|
99
|
+
schemaVersion: 1,
|
|
100
|
+
clientRequestId: current.clientRequestId,
|
|
101
|
+
payloadHash: current.payloadHash,
|
|
102
|
+
action: current.action,
|
|
103
|
+
createdAt: current.createdAt,
|
|
104
|
+
};
|
|
105
|
+
await writeSecureJson(operationFile(this.appData, operationId), next);
|
|
106
|
+
return next;
|
|
107
|
+
});
|
|
87
108
|
}
|
|
88
109
|
async list() {
|
|
89
110
|
const files = await listJsonFiles(this.appData.operations);
|
|
@@ -72,6 +72,49 @@ function readWriteSetGateToken(reviewPacket) {
|
|
|
72
72
|
const token = reviewPacket.writeSetGateToken;
|
|
73
73
|
return typeof token === "string" && token.includes(":") ? token : undefined;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Prefer structured stdout JSON failure semantics (findings / message) over bare
|
|
77
|
+
* `exit N` when stderr is empty — spine audit and similar read CLIs write details
|
|
78
|
+
* to stdout JSON and exit non-zero with empty stderr.
|
|
79
|
+
*/
|
|
80
|
+
function summarizeReadCliFailure(result) {
|
|
81
|
+
const fromJson = summarizeJsonFailure(result.json);
|
|
82
|
+
if (fromJson)
|
|
83
|
+
return fromJson;
|
|
84
|
+
const stderr = (result.stderr ?? "").trim();
|
|
85
|
+
if (stderr)
|
|
86
|
+
return stderr;
|
|
87
|
+
return `exit ${result.exitCode}`;
|
|
88
|
+
}
|
|
89
|
+
function summarizeJsonFailure(json) {
|
|
90
|
+
if (!json || typeof json !== "object")
|
|
91
|
+
return undefined;
|
|
92
|
+
const raw = json;
|
|
93
|
+
if (Array.isArray(raw.findings)) {
|
|
94
|
+
const messages = raw.findings
|
|
95
|
+
.map((finding) => {
|
|
96
|
+
if (!finding || typeof finding !== "object")
|
|
97
|
+
return "";
|
|
98
|
+
const message = finding.message;
|
|
99
|
+
return typeof message === "string" ? message.trim() : "";
|
|
100
|
+
})
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
if (messages.length > 0) {
|
|
103
|
+
const head = messages.slice(0, 6).join("; ");
|
|
104
|
+
return messages.length > 6 ? `${head}…` : head;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const nestedError = raw.error;
|
|
108
|
+
if (nestedError && typeof nestedError === "object") {
|
|
109
|
+
const message = nestedError.message;
|
|
110
|
+
if (typeof message === "string" && message.trim())
|
|
111
|
+
return message.trim();
|
|
112
|
+
}
|
|
113
|
+
if (typeof raw.message === "string" && raw.message.trim()) {
|
|
114
|
+
return raw.message.trim();
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
75
118
|
async function runReadCli(ctx, args, command) {
|
|
76
119
|
const run = ctx.runCommand ?? ((a, o) => ctx.client.run(a, o));
|
|
77
120
|
try {
|
|
@@ -124,11 +167,24 @@ async function runReadCli(ctx, args, command) {
|
|
|
124
167
|
raw: result.json,
|
|
125
168
|
});
|
|
126
169
|
}
|
|
170
|
+
const message = summarizeReadCliFailure(result);
|
|
171
|
+
if (result.json && typeof result.json === "object") {
|
|
172
|
+
return buildOperatorResult({
|
|
173
|
+
command,
|
|
174
|
+
ok: false,
|
|
175
|
+
outcome: "rejected",
|
|
176
|
+
result: result.json,
|
|
177
|
+
error: {
|
|
178
|
+
code: "INTERNAL_ERROR",
|
|
179
|
+
message,
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
127
183
|
return operatorFailed({
|
|
128
184
|
command,
|
|
129
185
|
outcome: "rejected",
|
|
130
186
|
code: "INTERNAL_ERROR",
|
|
131
|
-
message
|
|
187
|
+
message,
|
|
132
188
|
});
|
|
133
189
|
}
|
|
134
190
|
catch (error) {
|