@nail00749/agent-gvozd 0.1.8 → 0.2.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/README.md +68 -11
- package/defaults/agents/back-deep.jsonc +1923 -6
- package/defaults/agents/back-fast.jsonc +1908 -3
- package/defaults/agents/front-deep.jsonc +1978 -17
- package/defaults/agents/front-fast.jsonc +1963 -14
- package/defaults/agents/git.jsonc +20 -0
- package/defaults/agents/master-trusted.jsonc +32 -0
- package/defaults/agents/master.jsonc +0 -1
- package/defaults/agents/review-deep.jsonc +2443 -18
- package/defaults/agents/review-fast.jsonc +2428 -15
- package/defaults/agents/security.jsonc +2213 -29
- package/defaults/prompts/back-deep.md +1 -1
- package/defaults/prompts/back-fast.md +1 -1
- package/defaults/prompts/devops.md +1 -1
- package/defaults/prompts/front-deep.md +1 -1
- package/defaults/prompts/front-fast.md +1 -1
- package/defaults/prompts/master-trusted.md +7 -0
- package/defaults/prompts/master.md +2 -2
- package/defaults/prompts/review-deep.md +3 -1
- package/defaults/prompts/review-fast.md +1 -1
- package/defaults/prompts/security.md +3 -1
- package/dist/cli.js +303 -114
- package/dist/index.js +271 -11
- package/dist/tui.js +1080 -0
- package/package.json +13 -3
- package/defaults/agents/verifier.jsonc +0 -1298
package/dist/index.js
CHANGED
|
@@ -78,16 +78,27 @@ function normalizeMcpName(name) {
|
|
|
78
78
|
return name.replaceAll(/[^A-Za-z0-9_-]/g, "_");
|
|
79
79
|
}
|
|
80
80
|
function wildcardMatch(pattern, value) {
|
|
81
|
-
let
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
let patternIndex = 0;
|
|
82
|
+
let valueIndex = 0;
|
|
83
|
+
let starPatternIndex = -1;
|
|
84
|
+
let restartValueIndex = 0;
|
|
85
|
+
while (valueIndex < value.length) {
|
|
86
|
+
if (patternIndex < pattern.length && pattern[patternIndex] === "*") {
|
|
87
|
+
starPatternIndex = patternIndex++;
|
|
88
|
+
restartValueIndex = valueIndex;
|
|
89
|
+
} else if (patternIndex < pattern.length && (pattern[patternIndex] === "?" || pattern[patternIndex] === value[valueIndex])) {
|
|
90
|
+
patternIndex++;
|
|
91
|
+
valueIndex++;
|
|
92
|
+
} else if (starPatternIndex >= 0) {
|
|
93
|
+
patternIndex = starPatternIndex + 1;
|
|
94
|
+
valueIndex = ++restartValueIndex;
|
|
95
|
+
} else {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
89
98
|
}
|
|
90
|
-
|
|
99
|
+
while (patternIndex < pattern.length && pattern[patternIndex] === "*")
|
|
100
|
+
patternIndex++;
|
|
101
|
+
return patternIndex === pattern.length;
|
|
91
102
|
}
|
|
92
103
|
function explicitMcpAccess(agent, action, resources) {
|
|
93
104
|
if (resources.length === 0)
|
|
@@ -6100,6 +6111,14 @@ class FileLeaseManager {
|
|
|
6100
6111
|
}
|
|
6101
6112
|
return canonical;
|
|
6102
6113
|
}
|
|
6114
|
+
snapshot() {
|
|
6115
|
+
const leases = [...this.leases.values()];
|
|
6116
|
+
return leases.map((lease) => this.toStatus(lease)).sort((a, b) => {
|
|
6117
|
+
if (a.state === "active" !== (b.state === "active"))
|
|
6118
|
+
return a.state === "active" ? -1 : 1;
|
|
6119
|
+
return a.expiresAt - b.expiresAt;
|
|
6120
|
+
});
|
|
6121
|
+
}
|
|
6103
6122
|
toStatus(lease) {
|
|
6104
6123
|
return {
|
|
6105
6124
|
leaseId: lease.leaseId,
|
|
@@ -6354,6 +6373,190 @@ function loadConfig(projectDirectory, options = {}) {
|
|
|
6354
6373
|
};
|
|
6355
6374
|
}
|
|
6356
6375
|
|
|
6376
|
+
// src/permissions-rpc.ts
|
|
6377
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
6378
|
+
var GvozdPermissions = Rpc.define({
|
|
6379
|
+
id: "gvozd-permissions",
|
|
6380
|
+
events: {},
|
|
6381
|
+
methods: {
|
|
6382
|
+
evaluate: {
|
|
6383
|
+
input: {
|
|
6384
|
+
type: "object",
|
|
6385
|
+
properties: {
|
|
6386
|
+
agent: { type: "string" },
|
|
6387
|
+
checks: {
|
|
6388
|
+
type: "array",
|
|
6389
|
+
items: {
|
|
6390
|
+
type: "object",
|
|
6391
|
+
properties: {
|
|
6392
|
+
action: { type: "string" },
|
|
6393
|
+
resources: { type: "array", items: { type: "string" } }
|
|
6394
|
+
},
|
|
6395
|
+
required: ["action", "resources"],
|
|
6396
|
+
additionalProperties: false
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
},
|
|
6400
|
+
required: ["agent", "checks"],
|
|
6401
|
+
additionalProperties: false
|
|
6402
|
+
},
|
|
6403
|
+
output: {
|
|
6404
|
+
type: "object",
|
|
6405
|
+
properties: {
|
|
6406
|
+
results: {
|
|
6407
|
+
type: "array",
|
|
6408
|
+
items: {
|
|
6409
|
+
type: "object",
|
|
6410
|
+
properties: {
|
|
6411
|
+
action: { type: "string" },
|
|
6412
|
+
resource: { type: "string" },
|
|
6413
|
+
effect: { type: "string", enum: ["allow", "ask", "deny", "unknown"] },
|
|
6414
|
+
matchedRule: { type: ["string", "null"] }
|
|
6415
|
+
},
|
|
6416
|
+
required: ["action", "resource", "effect", "matchedRule"],
|
|
6417
|
+
additionalProperties: false
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6420
|
+
},
|
|
6421
|
+
required: ["results"],
|
|
6422
|
+
additionalProperties: false
|
|
6423
|
+
}
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
});
|
|
6427
|
+
var GvozdLeases = Rpc.define({
|
|
6428
|
+
id: "gvozd-leases",
|
|
6429
|
+
events: {},
|
|
6430
|
+
methods: {
|
|
6431
|
+
list: {
|
|
6432
|
+
input: {
|
|
6433
|
+
type: "object",
|
|
6434
|
+
properties: {},
|
|
6435
|
+
additionalProperties: false
|
|
6436
|
+
},
|
|
6437
|
+
output: {
|
|
6438
|
+
type: "object",
|
|
6439
|
+
properties: {
|
|
6440
|
+
leases: {
|
|
6441
|
+
type: "array",
|
|
6442
|
+
items: {
|
|
6443
|
+
type: "object",
|
|
6444
|
+
properties: {
|
|
6445
|
+
leaseId: { type: "string" },
|
|
6446
|
+
parentSessionID: { type: "string" },
|
|
6447
|
+
sessionID: { type: "string" },
|
|
6448
|
+
agent: { type: "string" },
|
|
6449
|
+
label: { type: "string" },
|
|
6450
|
+
state: { type: "string", enum: ["reserved", "active"] },
|
|
6451
|
+
files: { type: "array", items: { type: "string" } },
|
|
6452
|
+
expiresAt: { type: "number" },
|
|
6453
|
+
lastActivityAt: { type: "number" }
|
|
6454
|
+
},
|
|
6455
|
+
required: ["leaseId", "parentSessionID", "agent", "label", "state", "files", "expiresAt", "lastActivityAt"],
|
|
6456
|
+
additionalProperties: false
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
},
|
|
6460
|
+
required: ["leases"],
|
|
6461
|
+
additionalProperties: false
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
});
|
|
6466
|
+
function evaluateEffect(rules, action, resource) {
|
|
6467
|
+
let matched;
|
|
6468
|
+
for (const rule of rules) {
|
|
6469
|
+
if (wildcardMatch(rule.action, action) && wildcardMatch(rule.resource, resource))
|
|
6470
|
+
matched = rule;
|
|
6471
|
+
}
|
|
6472
|
+
if (!matched)
|
|
6473
|
+
return { effect: "unknown", matchedRule: null };
|
|
6474
|
+
return { effect: matched.effect, matchedRule: `${matched.action} ${matched.resource}` };
|
|
6475
|
+
}
|
|
6476
|
+
function evaluateInput(agentRules, input) {
|
|
6477
|
+
const results = input.checks.flatMap((request) => (request.resources.length > 0 ? request.resources : ["*"]).map((resource) => {
|
|
6478
|
+
const evaluation = agentRules ? evaluateEffect(agentRules, request.action, resource) : { effect: "unknown", matchedRule: null };
|
|
6479
|
+
return {
|
|
6480
|
+
action: request.action,
|
|
6481
|
+
resource,
|
|
6482
|
+
effect: evaluation.effect,
|
|
6483
|
+
matchedRule: evaluation.matchedRule
|
|
6484
|
+
};
|
|
6485
|
+
}));
|
|
6486
|
+
return { results };
|
|
6487
|
+
}
|
|
6488
|
+
|
|
6489
|
+
// src/trusted-mode.ts
|
|
6490
|
+
import { Rpc as Rpc2 } from "@opencode/plugin/rpc";
|
|
6491
|
+
var TRUST_MODES = ["balanced", "trusted", "strict"];
|
|
6492
|
+
var GvozdMode = Rpc2.define({
|
|
6493
|
+
id: "gvozd-mode",
|
|
6494
|
+
events: {},
|
|
6495
|
+
methods: {
|
|
6496
|
+
set: {
|
|
6497
|
+
input: {
|
|
6498
|
+
type: "object",
|
|
6499
|
+
properties: {
|
|
6500
|
+
sessionID: { type: "string" },
|
|
6501
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6502
|
+
},
|
|
6503
|
+
required: ["sessionID", "mode"],
|
|
6504
|
+
additionalProperties: false
|
|
6505
|
+
},
|
|
6506
|
+
output: {
|
|
6507
|
+
type: "object",
|
|
6508
|
+
properties: {
|
|
6509
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6510
|
+
},
|
|
6511
|
+
required: ["mode"],
|
|
6512
|
+
additionalProperties: false
|
|
6513
|
+
}
|
|
6514
|
+
},
|
|
6515
|
+
get: {
|
|
6516
|
+
input: {
|
|
6517
|
+
type: "object",
|
|
6518
|
+
properties: { sessionID: { type: "string" } },
|
|
6519
|
+
required: ["sessionID"],
|
|
6520
|
+
additionalProperties: false
|
|
6521
|
+
},
|
|
6522
|
+
output: {
|
|
6523
|
+
type: "object",
|
|
6524
|
+
properties: {
|
|
6525
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6526
|
+
},
|
|
6527
|
+
required: ["mode"],
|
|
6528
|
+
additionalProperties: false
|
|
6529
|
+
}
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6532
|
+
});
|
|
6533
|
+
function modePermissions(mode) {
|
|
6534
|
+
if (mode === "trusted") {
|
|
6535
|
+
return [
|
|
6536
|
+
{ action: "shell", resource: "*", effect: "allow" },
|
|
6537
|
+
{ action: "edit", resource: "*", effect: "allow" },
|
|
6538
|
+
{ action: "shell", resource: "git push --force*", effect: "deny" },
|
|
6539
|
+
{ action: "shell", resource: "git push -f*", effect: "deny" },
|
|
6540
|
+
{ action: "shell", resource: "git reset --hard*", effect: "deny" },
|
|
6541
|
+
{ action: "shell", resource: "git clean*", effect: "deny" },
|
|
6542
|
+
{ action: "shell", resource: "git filter-branch*", effect: "deny" },
|
|
6543
|
+
{ action: "shell", resource: "git filter-repo*", effect: "deny" },
|
|
6544
|
+
{ action: "shell", resource: "git rebase*", effect: "deny" },
|
|
6545
|
+
{ action: "shell", resource: "git checkout --*", effect: "deny" },
|
|
6546
|
+
{ action: "shell", resource: "git restore*", effect: "deny" },
|
|
6547
|
+
{ action: "shell", resource: "git rebase --abort*", effect: "allow" },
|
|
6548
|
+
{ action: "shell", resource: "git rebase --continue*", effect: "allow" }
|
|
6549
|
+
];
|
|
6550
|
+
}
|
|
6551
|
+
if (mode === "strict") {
|
|
6552
|
+
return [
|
|
6553
|
+
{ action: "shell", resource: "*", effect: "ask" },
|
|
6554
|
+
{ action: "edit", resource: "*", effect: "ask" }
|
|
6555
|
+
];
|
|
6556
|
+
}
|
|
6557
|
+
return [];
|
|
6558
|
+
}
|
|
6559
|
+
|
|
6357
6560
|
// src/file-lease-plugin.ts
|
|
6358
6561
|
var GVOZD_LEASE_TOOL = "gvozd_lease";
|
|
6359
6562
|
var GVOZD_CLAIM_TOOL = "gvozd_claim";
|
|
@@ -6426,7 +6629,9 @@ function enforceFileLeasePermission(event, config, manager) {
|
|
|
6426
6629
|
const role = roleOf(config, event.agent);
|
|
6427
6630
|
if (event.action === "edit") {
|
|
6428
6631
|
if (!role) {
|
|
6429
|
-
|
|
6632
|
+
if (event.agent && !manager.hasActiveLeases())
|
|
6633
|
+
return false;
|
|
6634
|
+
return deny(event, event.agent ? `Agent ${event.agent} has no configured file lease role; edits are allowed only while no writer leases are active` : "OpenCode supplied no agent identity for this mutation; the write is denied");
|
|
6430
6635
|
}
|
|
6431
6636
|
if (role === "readonly")
|
|
6432
6637
|
return deny(event, `Agent ${event.agent} is readonly and cannot mutate project files`);
|
|
@@ -6440,9 +6645,15 @@ function enforceFileLeasePermission(event, config, manager) {
|
|
|
6440
6645
|
}
|
|
6441
6646
|
if (event.action !== "shell" && event.action !== "bash")
|
|
6442
6647
|
return false;
|
|
6443
|
-
if (role === "coordinator"
|
|
6648
|
+
if (role === "coordinator") {
|
|
6444
6649
|
return deny(event, `Agent ${event.agent} cannot use shell while file leases enforce structured mutations`);
|
|
6445
6650
|
}
|
|
6651
|
+
if (role === "writer" && safeReadonlyShell(event.agent, event.resources)) {
|
|
6652
|
+
return false;
|
|
6653
|
+
}
|
|
6654
|
+
if (role === "writer") {
|
|
6655
|
+
return deny(event, `Agent ${event.agent} cannot use shell beyond read-only verification while file leases enforce structured mutations`);
|
|
6656
|
+
}
|
|
6446
6657
|
if (manager.hasActiveLeases() && !(role === "readonly" && safeReadonlyShell(event.agent, event.resources))) {
|
|
6447
6658
|
return deny(event, "Shell commands are paused until all active writer file leases are released");
|
|
6448
6659
|
}
|
|
@@ -6706,6 +6917,55 @@ var src_default = Plugin.define({
|
|
|
6706
6917
|
try {
|
|
6707
6918
|
const fileLeases = await installFileLeaseRuntime(ctx, config, { caseInsensitive });
|
|
6708
6919
|
resources.push(fileLeases);
|
|
6920
|
+
if (ctx.rpc && typeof ctx.rpc.register === "function") {
|
|
6921
|
+
const sessionModes = new Map;
|
|
6922
|
+
const defaultMode = () => "balanced";
|
|
6923
|
+
const modeRpc = await ctx.rpc.register(GvozdMode, {
|
|
6924
|
+
set: async (raw) => {
|
|
6925
|
+
const input = raw;
|
|
6926
|
+
sessionModes.set(input.sessionID, input.mode);
|
|
6927
|
+
if (typeof ctx.permission.rules === "function") {
|
|
6928
|
+
await ctx.permission.rules({
|
|
6929
|
+
sessionID: input.sessionID,
|
|
6930
|
+
permissions: modePermissions(input.mode)
|
|
6931
|
+
});
|
|
6932
|
+
}
|
|
6933
|
+
return { mode: input.mode };
|
|
6934
|
+
},
|
|
6935
|
+
get: async (raw) => {
|
|
6936
|
+
const input = raw;
|
|
6937
|
+
return { mode: sessionModes.get(input.sessionID) ?? defaultMode() };
|
|
6938
|
+
}
|
|
6939
|
+
});
|
|
6940
|
+
resources.push(modeRpc);
|
|
6941
|
+
const leasesRpc = await ctx.rpc.register(GvozdLeases, {
|
|
6942
|
+
list: async () => {
|
|
6943
|
+
const output = {
|
|
6944
|
+
leases: fileLeases.manager.snapshot().map((lease) => ({
|
|
6945
|
+
leaseId: lease.leaseId,
|
|
6946
|
+
parentSessionID: lease.parentSessionID,
|
|
6947
|
+
...lease.sessionID ? { sessionID: lease.sessionID } : {},
|
|
6948
|
+
agent: lease.agent,
|
|
6949
|
+
label: lease.label,
|
|
6950
|
+
state: lease.state,
|
|
6951
|
+
files: [...lease.files],
|
|
6952
|
+
expiresAt: lease.expiresAt,
|
|
6953
|
+
lastActivityAt: lease.lastActivityAt
|
|
6954
|
+
}))
|
|
6955
|
+
};
|
|
6956
|
+
return output;
|
|
6957
|
+
}
|
|
6958
|
+
});
|
|
6959
|
+
resources.push(leasesRpc);
|
|
6960
|
+
const permissionRpc = await ctx.rpc.register(GvozdPermissions, {
|
|
6961
|
+
evaluate: async (raw) => {
|
|
6962
|
+
const input = raw;
|
|
6963
|
+
const agent = config.agents[input.agent];
|
|
6964
|
+
return evaluateInput(agent && !agent.disabled ? agent.permissions : undefined, input);
|
|
6965
|
+
}
|
|
6966
|
+
});
|
|
6967
|
+
resources.push(permissionRpc);
|
|
6968
|
+
}
|
|
6709
6969
|
const agentTransform = await ctx.agent.transform((agents) => {
|
|
6710
6970
|
applyAgentConfiguration(agents, config, models.data, mcpServers);
|
|
6711
6971
|
});
|