@vincemakes/kiso-core 0.25.0 → 0.26.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/dist/kernel/loop.js +69 -5
- package/package.json +2 -2
package/dist/kernel/loop.js
CHANGED
|
@@ -959,12 +959,76 @@ async function decideCall(call, registry, hooks, ctx, log, resolveApproval, reso
|
|
|
959
959
|
// its verdict. A throwing chain counts as ask. Allow/deny are recorded
|
|
960
960
|
// durably with decidedBy = the deciding extension, never pausing for a
|
|
961
961
|
// human.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
962
|
+
// CX-1 F1 (audit F1): a durable decision is consumable ONCE — by the
|
|
963
|
+
// invocation it was recorded for, or by that invocation's RE-ISSUE on
|
|
964
|
+
// resume (same callId, same tool, identical input, and the original
|
|
965
|
+
// never produced a result). A provider callId alone is correlation,
|
|
966
|
+
// never identity: the old lookup found the current call itself
|
|
967
|
+
// (already appended), compared its input to its own, and took the
|
|
968
|
+
// FIRST decision with that callId in the whole log — so a reused
|
|
969
|
+
// callId inherited an old allow for a new call.
|
|
970
|
+
//
|
|
971
|
+
// The decision's invocationSeq names the original call (every
|
|
972
|
+
// producer stamps it, round 5). A legacy decision (no invocationSeq,
|
|
973
|
+
// pre-round-5 logs) binds to the nearest PRECEDING same-callId call
|
|
974
|
+
// before it — call first, decision after — with no run boundary
|
|
975
|
+
// between; one that binds to nothing is ambiguous, and the call goes
|
|
976
|
+
// to ask, never to the chain's allow.
|
|
977
|
+
const all = log.all;
|
|
978
|
+
const legacyBinding = (d) => {
|
|
979
|
+
for (let i = all.length - 1; i >= 0; i -= 1) {
|
|
980
|
+
const e = all[i];
|
|
981
|
+
if (e.seq >= d.seq)
|
|
982
|
+
continue;
|
|
983
|
+
if (e.type === "terminal")
|
|
984
|
+
return undefined;
|
|
985
|
+
if (e.type === "tool_call_end" && e.callId === call.callId)
|
|
986
|
+
return e.seq;
|
|
987
|
+
}
|
|
988
|
+
return undefined;
|
|
989
|
+
};
|
|
990
|
+
/** The re-issue rule: a resume re-issues the pending call under the SAME
|
|
991
|
+
* provider id — same callId, same tool, identical input — and the
|
|
992
|
+
* original never completed: no result and no other call under that id
|
|
993
|
+
* stands between the original and this call. A different callId with
|
|
994
|
+
* the same arguments is a new invocation (the /mode-switch shape: a
|
|
995
|
+
* write denied under plan is asked afresh under default). */
|
|
996
|
+
const reissues = (origSeq) => {
|
|
997
|
+
const orig = all.find((e) => e.seq === origSeq);
|
|
998
|
+
if (orig === undefined || orig.type !== "tool_call_end")
|
|
999
|
+
return false;
|
|
1000
|
+
if (orig.callId !== call.callId || orig.name !== call.name)
|
|
1001
|
+
return false;
|
|
1002
|
+
if (JSON.stringify(orig.input) !== JSON.stringify(call.input))
|
|
1003
|
+
return false;
|
|
1004
|
+
for (const e of all) {
|
|
1005
|
+
if (e.seq <= origSeq || e.seq >= call.seq)
|
|
1006
|
+
continue;
|
|
1007
|
+
if ((e.type === "tool_result" || e.type === "tool_call_end") && e.callId === orig.callId)
|
|
1008
|
+
return false;
|
|
1009
|
+
}
|
|
1010
|
+
return true;
|
|
1011
|
+
};
|
|
1012
|
+
let durable;
|
|
1013
|
+
let legacyAmbiguous = false;
|
|
1014
|
+
for (const e of all) {
|
|
1015
|
+
if (e.type !== "permission_decided" || e.decidedBy === undefined)
|
|
1016
|
+
continue;
|
|
1017
|
+
if (e.invocationSeq === undefined && e.callId !== call.callId)
|
|
1018
|
+
continue;
|
|
1019
|
+
const origSeq = e.invocationSeq ?? legacyBinding(e);
|
|
1020
|
+
if (origSeq === undefined) {
|
|
1021
|
+
legacyAmbiguous = true;
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (origSeq === call.seq || reissues(origSeq))
|
|
1025
|
+
durable = e;
|
|
1026
|
+
}
|
|
966
1027
|
let chainVerdict;
|
|
967
|
-
if (durable === undefined &&
|
|
1028
|
+
if (durable === undefined && legacyAmbiguous) {
|
|
1029
|
+
chainVerdict = { action: "ask" }; // the conservative reading of an old log
|
|
1030
|
+
}
|
|
1031
|
+
else if (durable === undefined && approvalPolicy !== undefined) {
|
|
968
1032
|
try {
|
|
969
1033
|
chainVerdict = await raceAbort(Promise.resolve(approvalPolicy.decide(payload, ctx)), signal);
|
|
970
1034
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincemakes/kiso-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "kiso (foundation) core — protocol, event log, loop, hooks, modes, permissions, compaction, delivery truth. The 2,000-line kernel at the bottom of the kiso framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"openai"
|
|
34
34
|
],
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@vincemakes/kiso-evals": "0.
|
|
36
|
+
"@vincemakes/kiso-evals": "0.26.1",
|
|
37
37
|
"@types/node": "^26.1.2",
|
|
38
38
|
"typescript": "^5.7.2",
|
|
39
39
|
"vitest": "^3.0.0"
|