midsummer-sol 0.2.2 → 0.3.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/index.js +200 -6
- package/package.json +1 -1
- package/sol-mcp.js +1742 -164
- package/sol.js +2239 -299
package/index.js
CHANGED
|
@@ -1423,16 +1423,76 @@ function trustAuthor(op, trust = {}) {
|
|
|
1423
1423
|
}
|
|
1424
1424
|
|
|
1425
1425
|
// src/prov.ts
|
|
1426
|
+
var TC_SEP = "\x01";
|
|
1427
|
+
var TC_TAG = "tc1";
|
|
1428
|
+
function encodeTool(p) {
|
|
1429
|
+
const surface = p.tool?.trim() || "";
|
|
1430
|
+
if (!p.toolCall && !p.testRunId)
|
|
1431
|
+
return surface || undefined;
|
|
1432
|
+
const segs = [surface, TC_TAG];
|
|
1433
|
+
const tc = p.toolCall;
|
|
1434
|
+
segs.push(`name=${tc?.name ?? ""}`, `args=${tc?.argsHash ?? ""}`, `inv=${tc?.invocationId ?? ""}`, `test=${p.testRunId ?? ""}`);
|
|
1435
|
+
return segs.join(TC_SEP);
|
|
1436
|
+
}
|
|
1437
|
+
function decodeTool(tool) {
|
|
1438
|
+
if (!tool)
|
|
1439
|
+
return {};
|
|
1440
|
+
const parts = tool.split(TC_SEP);
|
|
1441
|
+
if (parts.length < 2 || parts[1] !== TC_TAG)
|
|
1442
|
+
return { tool: tool || undefined };
|
|
1443
|
+
const surface = parts[0] || undefined;
|
|
1444
|
+
const kv = new Map;
|
|
1445
|
+
for (const seg of parts.slice(2)) {
|
|
1446
|
+
const eq2 = seg.indexOf("=");
|
|
1447
|
+
if (eq2 >= 0)
|
|
1448
|
+
kv.set(seg.slice(0, eq2), seg.slice(eq2 + 1));
|
|
1449
|
+
}
|
|
1450
|
+
const name = kv.get("name") || "";
|
|
1451
|
+
const argsHash = kv.get("args") || "";
|
|
1452
|
+
const invocationId = kv.get("inv") || "";
|
|
1453
|
+
const testRunId = kv.get("test") || undefined;
|
|
1454
|
+
const out = { tool: surface };
|
|
1455
|
+
if (name || argsHash || invocationId)
|
|
1456
|
+
out.toolCall = { name, argsHash, invocationId };
|
|
1457
|
+
if (testRunId)
|
|
1458
|
+
out.testRunId = testRunId;
|
|
1459
|
+
return out;
|
|
1460
|
+
}
|
|
1461
|
+
function parseToolCall(raw) {
|
|
1462
|
+
if (!raw)
|
|
1463
|
+
return;
|
|
1464
|
+
let v;
|
|
1465
|
+
try {
|
|
1466
|
+
v = JSON.parse(raw);
|
|
1467
|
+
} catch {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (!v || typeof v !== "object")
|
|
1471
|
+
return;
|
|
1472
|
+
const o = v;
|
|
1473
|
+
const name = typeof o.name === "string" ? o.name : "";
|
|
1474
|
+
const argsHash = typeof o.argsHash === "string" ? o.argsHash : "";
|
|
1475
|
+
const invocationId = typeof o.invocationId === "string" ? o.invocationId : "";
|
|
1476
|
+
if (!name || !argsHash || !invocationId)
|
|
1477
|
+
return;
|
|
1478
|
+
return { name, argsHash, invocationId };
|
|
1479
|
+
}
|
|
1426
1480
|
function provenanceFromEnv(env = process.env) {
|
|
1427
1481
|
const kind = env.SOL_KIND === "agent" ? "agent" : "human";
|
|
1482
|
+
const tool = encodeTool({
|
|
1483
|
+
tool: env.SOL_TOOL,
|
|
1484
|
+
toolCall: parseToolCall(env.SOL_TOOL_CALL),
|
|
1485
|
+
testRunId: env.SOL_TEST_RUN_ID || undefined
|
|
1486
|
+
});
|
|
1428
1487
|
const node = buildProvNode({
|
|
1429
1488
|
agentId: env.SOL_AGENT_ID || (kind === "agent" ? env.SOL_ACTOR : undefined),
|
|
1430
1489
|
sessionId: env.SOL_SESSION,
|
|
1431
1490
|
model: env.SOL_MODEL,
|
|
1432
1491
|
intent: env.SOL_INTENT,
|
|
1433
|
-
tool
|
|
1492
|
+
tool
|
|
1434
1493
|
});
|
|
1435
|
-
|
|
1494
|
+
const opRunId = env.SOL_OPERATION_ID || undefined;
|
|
1495
|
+
return { kind, ...node ? { node } : {}, ...opRunId ? { opRunId } : {} };
|
|
1436
1496
|
}
|
|
1437
1497
|
function buildProvNode(fields) {
|
|
1438
1498
|
const clean = { kind: "prov" };
|
|
@@ -1461,6 +1521,9 @@ function authorLabel(op, reader) {
|
|
|
1461
1521
|
const bits = [];
|
|
1462
1522
|
if (p?.sessionId)
|
|
1463
1523
|
bits.push(`session ${p.sessionId}`);
|
|
1524
|
+
const tp = p ? decodeTool(p.tool) : {};
|
|
1525
|
+
if (tp.toolCall)
|
|
1526
|
+
bits.push(`via ${tp.toolCall.name}`);
|
|
1464
1527
|
const tail = p?.intent ? ` — intent: ${p.intent}` : "";
|
|
1465
1528
|
return `${who}${bits.length ? ` (${bits.join(", ")})` : ""}${tail}`;
|
|
1466
1529
|
}
|
|
@@ -1470,10 +1533,21 @@ function provJson(op, reader, key, trust) {
|
|
|
1470
1533
|
return;
|
|
1471
1534
|
const out = { kind: op.kind ?? "human" };
|
|
1472
1535
|
if (p) {
|
|
1473
|
-
for (const k of ["agentId", "sessionId", "model", "intent", "
|
|
1536
|
+
for (const k of ["agentId", "sessionId", "model", "intent", "rationale", "parent"])
|
|
1474
1537
|
if (p[k])
|
|
1475
1538
|
out[k] = p[k];
|
|
1476
1539
|
}
|
|
1540
|
+
if (p?.tool) {
|
|
1541
|
+
const tp = decodeTool(p.tool);
|
|
1542
|
+
if (tp.tool)
|
|
1543
|
+
out.tool = tp.tool;
|
|
1544
|
+
if (tp.toolCall)
|
|
1545
|
+
out.toolCall = tp.toolCall;
|
|
1546
|
+
if (tp.testRunId)
|
|
1547
|
+
out.testRunId = tp.testRunId;
|
|
1548
|
+
}
|
|
1549
|
+
if (op.opRunId !== undefined)
|
|
1550
|
+
out.opRunId = op.opRunId;
|
|
1477
1551
|
if (op.sig !== undefined) {
|
|
1478
1552
|
const t = trustAuthor(op, trust);
|
|
1479
1553
|
out.signed = true;
|
|
@@ -1531,7 +1605,7 @@ function signTrailerValue(op, trust) {
|
|
|
1531
1605
|
return `${t.fingerprint} verified`;
|
|
1532
1606
|
}
|
|
1533
1607
|
}
|
|
1534
|
-
function provTrailers(op, reader, key, trust) {
|
|
1608
|
+
function provTrailers(op, reader, key, trust, reviewCount) {
|
|
1535
1609
|
const p = reader ? provOf(op, reader) : undefined;
|
|
1536
1610
|
const lines2 = [];
|
|
1537
1611
|
const signed = trustAuthor(op, trust);
|
|
@@ -1545,6 +1619,13 @@ function provTrailers(op, reader, key, trust) {
|
|
|
1545
1619
|
lines2.push(`Sol-Session: ${p.sessionId}`);
|
|
1546
1620
|
if (p?.model)
|
|
1547
1621
|
lines2.push(`Sol-Model: ${p.model}`);
|
|
1622
|
+
const tp = decodeTool(p?.tool);
|
|
1623
|
+
if (tp.toolCall)
|
|
1624
|
+
lines2.push(`Sol-ToolCall: ${tp.toolCall.name} args=${tp.toolCall.argsHash} inv=${tp.toolCall.invocationId}`);
|
|
1625
|
+
if (tp.testRunId)
|
|
1626
|
+
lines2.push(`Sol-TestRun: ${tp.testRunId}`);
|
|
1627
|
+
if (op.opRunId)
|
|
1628
|
+
lines2.push(`Sol-Operation: ${op.opRunId}`);
|
|
1548
1629
|
const intent = p?.intent || (isAgent ? op.message?.trim() || undefined : undefined);
|
|
1549
1630
|
if (intent)
|
|
1550
1631
|
lines2.push(`Sol-Intent: ${intent}`);
|
|
@@ -1553,15 +1634,40 @@ function provTrailers(op, reader, key, trust) {
|
|
|
1553
1634
|
lines2.push(`Sol-Sign: ${sign}`);
|
|
1554
1635
|
if (op.att !== undefined && !(key && !verifyAtt(key, op)))
|
|
1555
1636
|
lines2.push(`Sol-Attested: pushedBy=${op.pushedBy ?? "?"}`);
|
|
1637
|
+
if (reviewCount && reviewCount > 0)
|
|
1638
|
+
lines2.push(`Sol-Attestations: ${reviewCount}`);
|
|
1556
1639
|
return lines2;
|
|
1557
1640
|
}
|
|
1558
1641
|
async function captureProv(sink, env = process.env) {
|
|
1559
|
-
const { kind, node } = provenanceFromEnv(env);
|
|
1642
|
+
const { kind, node, opRunId } = provenanceFromEnv(env);
|
|
1560
1643
|
const out = {};
|
|
1561
1644
|
if (kind === "agent")
|
|
1562
1645
|
out.kind = "agent";
|
|
1563
1646
|
if (node)
|
|
1564
1647
|
out.prov = await sink.put(node);
|
|
1648
|
+
if (opRunId)
|
|
1649
|
+
out.opRunId = opRunId;
|
|
1650
|
+
return out;
|
|
1651
|
+
}
|
|
1652
|
+
function toolProvOf(op, reader) {
|
|
1653
|
+
const p = reader ? provOf(op, reader) : undefined;
|
|
1654
|
+
return p ? decodeTool(p.tool) : {};
|
|
1655
|
+
}
|
|
1656
|
+
function toAttestationRecords(op, reader, issuer = "sol") {
|
|
1657
|
+
const tp = toolProvOf(op, reader);
|
|
1658
|
+
const out = [];
|
|
1659
|
+
if (tp.toolCall) {
|
|
1660
|
+
out.push({
|
|
1661
|
+
kind: "audit",
|
|
1662
|
+
issuer,
|
|
1663
|
+
verdict: "pass",
|
|
1664
|
+
summary: `tool-call ${tp.toolCall.name} (inv ${tp.toolCall.invocationId}, args ${tp.toolCall.argsHash})`,
|
|
1665
|
+
at: op.at
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
if (tp.testRunId) {
|
|
1669
|
+
out.push({ kind: "check", issuer, verdict: "pending", summary: `test-run ${tp.testRunId}`, at: op.at });
|
|
1670
|
+
}
|
|
1565
1671
|
return out;
|
|
1566
1672
|
}
|
|
1567
1673
|
|
|
@@ -1613,10 +1719,25 @@ class AsyncRepo {
|
|
|
1613
1719
|
await this.log.append(by === this.actor ? this.sign(entry) : entry);
|
|
1614
1720
|
}
|
|
1615
1721
|
provFor;
|
|
1722
|
+
pendingToolCall;
|
|
1723
|
+
setToolCall(tc) {
|
|
1724
|
+
this.pendingToolCall = tc;
|
|
1725
|
+
}
|
|
1616
1726
|
async provenance(by) {
|
|
1617
1727
|
if (by !== this.actor)
|
|
1618
1728
|
return {};
|
|
1619
|
-
|
|
1729
|
+
const base = await (this.provFor ??= captureProv(this.store));
|
|
1730
|
+
const tc = this.pendingToolCall;
|
|
1731
|
+
if (!tc)
|
|
1732
|
+
return base;
|
|
1733
|
+
this.pendingToolCall = undefined;
|
|
1734
|
+
const { kind, node } = provenanceFromEnv();
|
|
1735
|
+
const session = node ?? { kind: "prov" };
|
|
1736
|
+
const surface = decodeTool(session.tool);
|
|
1737
|
+
const tool = encodeTool({ tool: surface.tool, toolCall: tc, testRunId: surface.testRunId });
|
|
1738
|
+
const merged = { ...session, kind: "prov", tool };
|
|
1739
|
+
const prov = await this.store.put(merged);
|
|
1740
|
+
return { ...kind === "agent" ? { kind: "agent" } : {}, prov, ...base.opRunId ? { opRunId: base.opRunId } : {} };
|
|
1620
1741
|
}
|
|
1621
1742
|
async currentRoot() {
|
|
1622
1743
|
const head = await this.log.head();
|
|
@@ -1818,6 +1939,9 @@ class SolWorkspace {
|
|
|
1818
1939
|
this.actor = actor;
|
|
1819
1940
|
this.repo = new AsyncRepo(store, log, actor);
|
|
1820
1941
|
}
|
|
1942
|
+
setToolCall(tc) {
|
|
1943
|
+
this.repo.setToolCall(tc);
|
|
1944
|
+
}
|
|
1821
1945
|
async write(path, content) {
|
|
1822
1946
|
return this.repo.writeFile(safePath(path), content);
|
|
1823
1947
|
}
|
|
@@ -2844,7 +2968,64 @@ class Ledger {
|
|
|
2844
2968
|
return [...this.bySubject.get(subject) ?? []];
|
|
2845
2969
|
}
|
|
2846
2970
|
}
|
|
2971
|
+
function canonicalReview(a) {
|
|
2972
|
+
return JSON.stringify({
|
|
2973
|
+
kind: a.kind,
|
|
2974
|
+
issuer: a.issuer,
|
|
2975
|
+
reviewerKind: a.reviewerKind,
|
|
2976
|
+
verdict: a.verdict,
|
|
2977
|
+
commitSha: a.commitSha,
|
|
2978
|
+
findings: a.findings ?? [],
|
|
2979
|
+
at: a.at
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
function signReview(key, a) {
|
|
2983
|
+
return createHmac2("sha256", key).update(canonicalReview(a)).digest("base64");
|
|
2984
|
+
}
|
|
2985
|
+
function verifyReview(key, a, sig = a.signature) {
|
|
2986
|
+
if (!sig)
|
|
2987
|
+
return false;
|
|
2988
|
+
const expected = Buffer.from(signReview(key, a));
|
|
2989
|
+
const got = Buffer.from(sig);
|
|
2990
|
+
return expected.length === got.length && timingSafeEqual3(expected, got);
|
|
2991
|
+
}
|
|
2992
|
+
function buildReviewerAttestation(key, a) {
|
|
2993
|
+
const full = { kind: "review", ...a, at: a.at ?? Date.now() };
|
|
2994
|
+
full.signature = signReview(key, full);
|
|
2995
|
+
return full;
|
|
2996
|
+
}
|
|
2997
|
+
function reviewSubject(changeId, commitSha) {
|
|
2998
|
+
return `review:${changeId}:${commitSha}`;
|
|
2999
|
+
}
|
|
3000
|
+
|
|
3001
|
+
class ReviewerAttestationLedger {
|
|
3002
|
+
byChange = new Map;
|
|
3003
|
+
append(changeId, a) {
|
|
3004
|
+
if (!a.signature)
|
|
3005
|
+
throw new Error("reviewer attestation must be signed before it is recorded");
|
|
3006
|
+
const list = this.byChange.get(changeId) ?? [];
|
|
3007
|
+
list.push(a);
|
|
3008
|
+
this.byChange.set(changeId, list);
|
|
3009
|
+
}
|
|
3010
|
+
provenance(changeId) {
|
|
3011
|
+
return [...this.byChange.get(changeId) ?? []];
|
|
3012
|
+
}
|
|
3013
|
+
forCommit(changeId, commitSha) {
|
|
3014
|
+
return this.provenance(changeId).filter((a) => a.commitSha === commitSha);
|
|
3015
|
+
}
|
|
3016
|
+
verifiedCount(key, changeId, commitSha) {
|
|
3017
|
+
const recs = commitSha ? this.forCommit(changeId, commitSha) : this.provenance(changeId);
|
|
3018
|
+
return recs.filter((a) => verifyReview(key, a)).length;
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
2847
3021
|
// src/check.ts
|
|
3022
|
+
function gateFromResult(head, runner, r, at = Date.now()) {
|
|
3023
|
+
return { head, verdict: r.verdict, checkName: runner.id, summary: r.summary, findings: r.findings, at };
|
|
3024
|
+
}
|
|
3025
|
+
function pendingGate(head, at = Date.now()) {
|
|
3026
|
+
return { head, verdict: "pending", at };
|
|
3027
|
+
}
|
|
3028
|
+
|
|
2848
3029
|
class CheckCache {
|
|
2849
3030
|
cache = new Map;
|
|
2850
3031
|
_hits = 0;
|
|
@@ -2871,6 +3052,7 @@ function toAttestation(runner, r) {
|
|
|
2871
3052
|
export {
|
|
2872
3053
|
writeFile,
|
|
2873
3054
|
wrapCekTo,
|
|
3055
|
+
verifyReview,
|
|
2874
3056
|
verifyPubkeySig,
|
|
2875
3057
|
verifyOp,
|
|
2876
3058
|
verifyAuthor,
|
|
@@ -2878,9 +3060,12 @@ export {
|
|
|
2878
3060
|
verifyAtt,
|
|
2879
3061
|
unwrapCekWith,
|
|
2880
3062
|
trustAuthor,
|
|
3063
|
+
toolProvOf,
|
|
3064
|
+
toAttestationRecords,
|
|
2881
3065
|
toAttestation,
|
|
2882
3066
|
signerFrom,
|
|
2883
3067
|
signTag,
|
|
3068
|
+
signReview,
|
|
2884
3069
|
signPubkey,
|
|
2885
3070
|
signOp,
|
|
2886
3071
|
signAuthor,
|
|
@@ -2894,6 +3079,7 @@ export {
|
|
|
2894
3079
|
safePath,
|
|
2895
3080
|
runCommand,
|
|
2896
3081
|
rotate,
|
|
3082
|
+
reviewSubject,
|
|
2897
3083
|
recoveryKeyPair,
|
|
2898
3084
|
readFile,
|
|
2899
3085
|
reachableFrom,
|
|
@@ -2907,6 +3093,8 @@ export {
|
|
|
2907
3093
|
provTrailers,
|
|
2908
3094
|
provOf,
|
|
2909
3095
|
provJson,
|
|
3096
|
+
pendingGate,
|
|
3097
|
+
parseToolCall,
|
|
2910
3098
|
pageOps,
|
|
2911
3099
|
openWithRecovery,
|
|
2912
3100
|
openContent,
|
|
@@ -2927,6 +3115,7 @@ export {
|
|
|
2927
3115
|
getTree,
|
|
2928
3116
|
generateRecoveryCode,
|
|
2929
3117
|
generateKeypair,
|
|
3118
|
+
gateFromResult,
|
|
2930
3119
|
garbage,
|
|
2931
3120
|
formatLog,
|
|
2932
3121
|
fingerprintOf,
|
|
@@ -2934,21 +3123,25 @@ export {
|
|
|
2934
3123
|
fileAt,
|
|
2935
3124
|
exportFiles,
|
|
2936
3125
|
entryKindAt,
|
|
3126
|
+
encodeTool,
|
|
2937
3127
|
emptyRoot,
|
|
2938
3128
|
duplicateExportCheck,
|
|
2939
3129
|
diffTrees,
|
|
2940
3130
|
diffFile,
|
|
2941
3131
|
deriveIdentity,
|
|
2942
3132
|
deleteFile,
|
|
3133
|
+
decodeTool,
|
|
2943
3134
|
converge,
|
|
2944
3135
|
compact,
|
|
2945
3136
|
clone,
|
|
2946
3137
|
ciphertextOf,
|
|
2947
3138
|
captureProv,
|
|
3139
|
+
canonicalReview,
|
|
2948
3140
|
canonicalOp,
|
|
2949
3141
|
canonicalAuthor,
|
|
2950
3142
|
canonicalAttestation,
|
|
2951
3143
|
canonicalAtt,
|
|
3144
|
+
buildReviewerAttestation,
|
|
2952
3145
|
buildProvNode,
|
|
2953
3146
|
blobHashAt,
|
|
2954
3147
|
blame,
|
|
@@ -2963,6 +3156,7 @@ export {
|
|
|
2963
3156
|
SealedClient,
|
|
2964
3157
|
SealRegistry,
|
|
2965
3158
|
SEALED,
|
|
3159
|
+
ReviewerAttestationLedger,
|
|
2966
3160
|
Repo,
|
|
2967
3161
|
RefAcls,
|
|
2968
3162
|
MemoryOpLog,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "midsummer-sol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Sol — agent-native version control (a new git). CLI, MCP server, and no-filesystem SDK.",
|
|
5
5
|
"bin": { "sol": "./sol.js", "sol-mcp": "./sol-mcp.js", "sol-secret-mcp": "./sol-secret-mcp.js" },
|
|
6
6
|
"main": "./index.js",
|