impel-cli 0.20.12 → 0.20.14
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/RELEASE_NOTES.md +18 -0
- package/package.json +1 -1
- package/src/nativeAgentTransport.js +65 -7
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## 0.20.14 — Opaque direct-answer continuations
|
|
4
|
+
|
|
5
|
+
- Returns a minimal local continuation locator for read-only direct answers,
|
|
6
|
+
so wrapper models no longer need to copy policy or request fingerprints.
|
|
7
|
+
- Resolves the locator against integrity-validated tenant-local state and the
|
|
8
|
+
MCP server's fixed agent binding; durable and write-capable handles retain
|
|
9
|
+
their full fail-closed validation.
|
|
10
|
+
- Adds a regression test for the production case where Claude shortened a
|
|
11
|
+
request fingerprint while preserving the invocation id.
|
|
12
|
+
|
|
13
|
+
## 0.20.13 — Allocation publication race
|
|
14
|
+
|
|
15
|
+
- Retries allocation when a competing process moves the published lock between
|
|
16
|
+
the expected create collision and inspection.
|
|
17
|
+
- Prevents a harmless `ENOENT` publication race from escaping as an MCP error
|
|
18
|
+
or consuming an agent inference turn.
|
|
19
|
+
- Adds a deterministic regression test for the exact production interleaving.
|
|
20
|
+
|
|
3
21
|
## 0.20.12 — Internal admission retry
|
|
4
22
|
|
|
5
23
|
- Absorbs brief local run-allocation lock contention inside the transport
|
package/package.json
CHANGED
|
@@ -31,6 +31,7 @@ export {
|
|
|
31
31
|
NATIVE_AGENT_RUN_TOOL,
|
|
32
32
|
};
|
|
33
33
|
export const NATIVE_AGENT_HANDLE_SCHEMA = "impel.native-agent-run.v1";
|
|
34
|
+
export const NATIVE_AGENT_CONTINUATION_SCHEMA = "impel.native-agent-continuation.v1";
|
|
34
35
|
export const NATIVE_AGENT_RESULT_SCHEMA = "impel.native-agent-result.v1";
|
|
35
36
|
export const NATIVE_AGENT_RECOVERY_SCHEMA = "impel.native-agent-recovery.v1";
|
|
36
37
|
|
|
@@ -390,7 +391,21 @@ function handleForState(state) {
|
|
|
390
391
|
};
|
|
391
392
|
}
|
|
392
393
|
|
|
394
|
+
function continuationForState(state) {
|
|
395
|
+
return {
|
|
396
|
+
schema: NATIVE_AGENT_CONTINUATION_SCHEMA,
|
|
397
|
+
invocationId: state.invocationId,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
393
401
|
function normalizeHandle(value) {
|
|
402
|
+
if (value?.schema === NATIVE_AGENT_CONTINUATION_SCHEMA) {
|
|
403
|
+
exactObject(value, ["schema", "invocationId"], "native-agent continuation");
|
|
404
|
+
if (typeof value.invocationId !== "string" || !SAFE_INVOCATION_RE.test(value.invocationId)) {
|
|
405
|
+
throw new Error("native-agent continuation invocationId is invalid");
|
|
406
|
+
}
|
|
407
|
+
return { ...value };
|
|
408
|
+
}
|
|
394
409
|
exactObject(value, [
|
|
395
410
|
"schema",
|
|
396
411
|
"tenantId",
|
|
@@ -1388,7 +1403,13 @@ export class NativeAgentCompositeTransport {
|
|
|
1388
1403
|
throw error;
|
|
1389
1404
|
}
|
|
1390
1405
|
fs.rmSync(generationPath, { recursive: true, force: true });
|
|
1391
|
-
|
|
1406
|
+
let stat;
|
|
1407
|
+
try {
|
|
1408
|
+
stat = fs.lstatSync(lockPath);
|
|
1409
|
+
} catch (statError) {
|
|
1410
|
+
if (statError?.code === "ENOENT") continue;
|
|
1411
|
+
throw statError;
|
|
1412
|
+
}
|
|
1392
1413
|
if (stat.isSymbolicLink()) throw new Error("refusing to use symlinked native-agent run lock");
|
|
1393
1414
|
let current = null;
|
|
1394
1415
|
let legacy = null;
|
|
@@ -1558,6 +1579,18 @@ export class NativeAgentCompositeTransport {
|
|
|
1558
1579
|
}
|
|
1559
1580
|
|
|
1560
1581
|
validateBoundState(state, handle) {
|
|
1582
|
+
if (handle.schema === NATIVE_AGENT_CONTINUATION_SCHEMA) {
|
|
1583
|
+
if (state.startMode !== "answer") {
|
|
1584
|
+
throw new Error("native-agent continuation does not identify a direct-answer run");
|
|
1585
|
+
}
|
|
1586
|
+
if (state.tenantId !== this.tenantId
|
|
1587
|
+
|| state.agentId !== this.agentId
|
|
1588
|
+
|| state.scopeParam !== this.scopeParam
|
|
1589
|
+
|| state.policyFingerprint !== this.policyFingerprint) {
|
|
1590
|
+
throw new Error("native-agent continuation does not match this fixed binding");
|
|
1591
|
+
}
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1561
1594
|
const expected = {
|
|
1562
1595
|
tenantId: this.tenantId,
|
|
1563
1596
|
agentId: this.agentId,
|
|
@@ -1774,7 +1807,9 @@ export class NativeAgentCompositeTransport {
|
|
|
1774
1807
|
runId: state.upstreamRunId,
|
|
1775
1808
|
};
|
|
1776
1809
|
}
|
|
1777
|
-
return result
|
|
1810
|
+
return result?.schema === NATIVE_AGENT_HANDLE_SCHEMA
|
|
1811
|
+
? continuationForState(state)
|
|
1812
|
+
: result;
|
|
1778
1813
|
} catch (error) {
|
|
1779
1814
|
if (!state) throw error;
|
|
1780
1815
|
if (lock) {
|
|
@@ -1784,7 +1819,7 @@ export class NativeAgentCompositeTransport {
|
|
|
1784
1819
|
// The persisted handle remains authoritative after a lost lease.
|
|
1785
1820
|
}
|
|
1786
1821
|
}
|
|
1787
|
-
return
|
|
1822
|
+
return continuationForState(state);
|
|
1788
1823
|
} finally {
|
|
1789
1824
|
lock?.release();
|
|
1790
1825
|
attachment.dispose();
|
|
@@ -1805,11 +1840,14 @@ export class NativeAgentCompositeTransport {
|
|
|
1805
1840
|
// upstream byte is sent.
|
|
1806
1841
|
lock = this.acquireLock(state.invocationId);
|
|
1807
1842
|
if (!lock) return handleForState(state);
|
|
1808
|
-
|
|
1843
|
+
const result = await this.attach(state, prepared, {
|
|
1809
1844
|
signal: attachment.signal,
|
|
1810
1845
|
onProgress,
|
|
1811
1846
|
lock,
|
|
1812
1847
|
});
|
|
1848
|
+
return state.startMode === "answer" && result?.schema === NATIVE_AGENT_HANDLE_SCHEMA
|
|
1849
|
+
? continuationForState(state)
|
|
1850
|
+
: result;
|
|
1813
1851
|
} catch (error) {
|
|
1814
1852
|
// Once state exists, cancellation or the local attachment deadline must
|
|
1815
1853
|
// hand the caller the only handle that can resume it.
|
|
@@ -1828,7 +1866,7 @@ export class NativeAgentCompositeTransport {
|
|
|
1828
1866
|
exactObject(args, ["handle"], "resume_native_agent_run arguments");
|
|
1829
1867
|
if (!Object.hasOwn(args, "handle")) throw new Error("resume_native_agent_run requires a handle");
|
|
1830
1868
|
const handle = normalizeHandle(args.handle);
|
|
1831
|
-
if (handle.tenantId !== this.tenantId) {
|
|
1869
|
+
if (handle.schema === NATIVE_AGENT_HANDLE_SCHEMA && handle.tenantId !== this.tenantId) {
|
|
1832
1870
|
throw new Error("native-agent run handle does not match this fixed tenant");
|
|
1833
1871
|
}
|
|
1834
1872
|
this.ensureRoots();
|
|
@@ -2124,7 +2162,7 @@ export class NativeAgentCompositeTransport {
|
|
|
2124
2162
|
result = resultForState(state);
|
|
2125
2163
|
if (result.schema === NATIVE_AGENT_RESULT_SCHEMA) return result;
|
|
2126
2164
|
}
|
|
2127
|
-
return handleForState(state);
|
|
2165
|
+
return state.startMode === "answer" ? continuationForState(state) : handleForState(state);
|
|
2128
2166
|
} catch (error) {
|
|
2129
2167
|
if (!state.upstreamRunId) throw error;
|
|
2130
2168
|
try {
|
|
@@ -2161,6 +2199,19 @@ const HANDLE_SCHEMA = {
|
|
|
2161
2199
|
},
|
|
2162
2200
|
};
|
|
2163
2201
|
|
|
2202
|
+
const ANSWER_RESUME_SCHEMA = {
|
|
2203
|
+
type: "object",
|
|
2204
|
+
additionalProperties: false,
|
|
2205
|
+
required: ["schema", "invocationId"],
|
|
2206
|
+
properties: {
|
|
2207
|
+
...HANDLE_SCHEMA.properties,
|
|
2208
|
+
schema: {
|
|
2209
|
+
type: "string",
|
|
2210
|
+
enum: [NATIVE_AGENT_CONTINUATION_SCHEMA, NATIVE_AGENT_HANDLE_SCHEMA],
|
|
2211
|
+
},
|
|
2212
|
+
},
|
|
2213
|
+
};
|
|
2214
|
+
|
|
2164
2215
|
export function nativeAgentCompositeTools({ mode = "durable" } = {}) {
|
|
2165
2216
|
if (!["durable", "recovery", "answer"].includes(mode)) throw new Error("invalid native-agent MCP mode");
|
|
2166
2217
|
const answerTool = {
|
|
@@ -2227,7 +2278,14 @@ export function nativeAgentCompositeTools({ mode = "durable" } = {}) {
|
|
|
2227
2278
|
// continuation handle. Once that handle exists, resume is the only valid
|
|
2228
2279
|
// next action; exposing recovery here gives the wrapper model an
|
|
2229
2280
|
// unnecessary branch and can add a fifth inference round to healthy runs.
|
|
2230
|
-
|
|
2281
|
+
const resumeTool = tools.find(({ name }) => name === NATIVE_AGENT_RESUME_TOOL);
|
|
2282
|
+
return [answerTool, {
|
|
2283
|
+
...resumeTool,
|
|
2284
|
+
inputSchema: {
|
|
2285
|
+
...resumeTool.inputSchema,
|
|
2286
|
+
properties: { handle: ANSWER_RESUME_SCHEMA },
|
|
2287
|
+
},
|
|
2288
|
+
}];
|
|
2231
2289
|
}
|
|
2232
2290
|
return mode === "recovery" ? tools.filter(({ name }) => name !== NATIVE_AGENT_RUN_TOOL) : tools;
|
|
2233
2291
|
}
|