@nail00749/agent-gvozd 0.1.7 → 0.2.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/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 +174 -58
- package/dist/index.js +261 -2
- package/dist/tui.js +956 -0
- package/package.json +13 -3
- package/defaults/agents/verifier.jsonc +0 -1298
package/dist/index.js
CHANGED
|
@@ -6100,6 +6100,14 @@ class FileLeaseManager {
|
|
|
6100
6100
|
}
|
|
6101
6101
|
return canonical;
|
|
6102
6102
|
}
|
|
6103
|
+
snapshot() {
|
|
6104
|
+
const leases = [...this.leases.values()];
|
|
6105
|
+
return leases.map((lease) => this.toStatus(lease)).sort((a, b) => {
|
|
6106
|
+
if (a.state === "active" !== (b.state === "active"))
|
|
6107
|
+
return a.state === "active" ? -1 : 1;
|
|
6108
|
+
return a.expiresAt - b.expiresAt;
|
|
6109
|
+
});
|
|
6110
|
+
}
|
|
6103
6111
|
toStatus(lease) {
|
|
6104
6112
|
return {
|
|
6105
6113
|
leaseId: lease.leaseId,
|
|
@@ -6354,6 +6362,200 @@ function loadConfig(projectDirectory, options = {}) {
|
|
|
6354
6362
|
};
|
|
6355
6363
|
}
|
|
6356
6364
|
|
|
6365
|
+
// src/permissions-rpc.ts
|
|
6366
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
6367
|
+
var GvozdPermissions = Rpc.define({
|
|
6368
|
+
id: "gvozd-permissions",
|
|
6369
|
+
events: {},
|
|
6370
|
+
methods: {
|
|
6371
|
+
evaluate: {
|
|
6372
|
+
input: {
|
|
6373
|
+
type: "object",
|
|
6374
|
+
properties: {
|
|
6375
|
+
agent: { type: "string" },
|
|
6376
|
+
checks: {
|
|
6377
|
+
type: "array",
|
|
6378
|
+
items: {
|
|
6379
|
+
type: "object",
|
|
6380
|
+
properties: {
|
|
6381
|
+
action: { type: "string" },
|
|
6382
|
+
resources: { type: "array", items: { type: "string" } }
|
|
6383
|
+
},
|
|
6384
|
+
required: ["action", "resources"],
|
|
6385
|
+
additionalProperties: false
|
|
6386
|
+
}
|
|
6387
|
+
}
|
|
6388
|
+
},
|
|
6389
|
+
required: ["agent", "checks"],
|
|
6390
|
+
additionalProperties: false
|
|
6391
|
+
},
|
|
6392
|
+
output: {
|
|
6393
|
+
type: "object",
|
|
6394
|
+
properties: {
|
|
6395
|
+
results: {
|
|
6396
|
+
type: "array",
|
|
6397
|
+
items: {
|
|
6398
|
+
type: "object",
|
|
6399
|
+
properties: {
|
|
6400
|
+
action: { type: "string" },
|
|
6401
|
+
resource: { type: "string" },
|
|
6402
|
+
effect: { type: "string", enum: ["allow", "ask", "deny", "unknown"] },
|
|
6403
|
+
matchedRule: { type: ["string", "null"] }
|
|
6404
|
+
},
|
|
6405
|
+
required: ["action", "resource", "effect", "matchedRule"],
|
|
6406
|
+
additionalProperties: false
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
},
|
|
6410
|
+
required: ["results"],
|
|
6411
|
+
additionalProperties: false
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6415
|
+
});
|
|
6416
|
+
var GvozdLeases = Rpc.define({
|
|
6417
|
+
id: "gvozd-leases",
|
|
6418
|
+
events: {},
|
|
6419
|
+
methods: {
|
|
6420
|
+
list: {
|
|
6421
|
+
input: {
|
|
6422
|
+
type: "object",
|
|
6423
|
+
properties: {},
|
|
6424
|
+
additionalProperties: false
|
|
6425
|
+
},
|
|
6426
|
+
output: {
|
|
6427
|
+
type: "object",
|
|
6428
|
+
properties: {
|
|
6429
|
+
leases: {
|
|
6430
|
+
type: "array",
|
|
6431
|
+
items: {
|
|
6432
|
+
type: "object",
|
|
6433
|
+
properties: {
|
|
6434
|
+
leaseId: { type: "string" },
|
|
6435
|
+
parentSessionID: { type: "string" },
|
|
6436
|
+
sessionID: { type: "string" },
|
|
6437
|
+
agent: { type: "string" },
|
|
6438
|
+
label: { type: "string" },
|
|
6439
|
+
state: { type: "string", enum: ["reserved", "active"] },
|
|
6440
|
+
files: { type: "array", items: { type: "string" } },
|
|
6441
|
+
expiresAt: { type: "number" },
|
|
6442
|
+
lastActivityAt: { type: "number" }
|
|
6443
|
+
},
|
|
6444
|
+
required: ["leaseId", "parentSessionID", "agent", "label", "state", "files", "expiresAt", "lastActivityAt"],
|
|
6445
|
+
additionalProperties: false
|
|
6446
|
+
}
|
|
6447
|
+
}
|
|
6448
|
+
},
|
|
6449
|
+
required: ["leases"],
|
|
6450
|
+
additionalProperties: false
|
|
6451
|
+
}
|
|
6452
|
+
}
|
|
6453
|
+
}
|
|
6454
|
+
});
|
|
6455
|
+
function evaluateEffect(rules, action, resource) {
|
|
6456
|
+
let matched;
|
|
6457
|
+
for (const rule of rules) {
|
|
6458
|
+
if (match(rule.action, action) && match(rule.resource, resource))
|
|
6459
|
+
matched = rule;
|
|
6460
|
+
}
|
|
6461
|
+
if (!matched)
|
|
6462
|
+
return { effect: "unknown", matchedRule: null };
|
|
6463
|
+
return { effect: matched.effect, matchedRule: `${matched.action} ${matched.resource}` };
|
|
6464
|
+
}
|
|
6465
|
+
function match(pattern, value) {
|
|
6466
|
+
let source = "^";
|
|
6467
|
+
for (const character of pattern) {
|
|
6468
|
+
if (character === "*")
|
|
6469
|
+
source += ".*";
|
|
6470
|
+
else if (character === "?")
|
|
6471
|
+
source += ".";
|
|
6472
|
+
else
|
|
6473
|
+
source += character.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
|
|
6474
|
+
}
|
|
6475
|
+
return new RegExp(`${source}$`).test(value);
|
|
6476
|
+
}
|
|
6477
|
+
function evaluateInput(agentRules, input) {
|
|
6478
|
+
const results = input.checks.flatMap((request) => (request.resources.length > 0 ? request.resources : ["*"]).map((resource) => {
|
|
6479
|
+
const evaluation = agentRules ? evaluateEffect(agentRules, request.action, resource) : { effect: "unknown", matchedRule: null };
|
|
6480
|
+
return {
|
|
6481
|
+
action: request.action,
|
|
6482
|
+
resource,
|
|
6483
|
+
effect: evaluation.effect,
|
|
6484
|
+
matchedRule: evaluation.matchedRule
|
|
6485
|
+
};
|
|
6486
|
+
}));
|
|
6487
|
+
return { results };
|
|
6488
|
+
}
|
|
6489
|
+
|
|
6490
|
+
// src/trusted-mode.ts
|
|
6491
|
+
import { Rpc as Rpc2 } from "@opencode/plugin/rpc";
|
|
6492
|
+
var TRUST_MODES = ["balanced", "trusted", "strict"];
|
|
6493
|
+
var GvozdMode = Rpc2.define({
|
|
6494
|
+
id: "gvozd-mode",
|
|
6495
|
+
events: {},
|
|
6496
|
+
methods: {
|
|
6497
|
+
set: {
|
|
6498
|
+
input: {
|
|
6499
|
+
type: "object",
|
|
6500
|
+
properties: {
|
|
6501
|
+
sessionID: { type: "string" },
|
|
6502
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6503
|
+
},
|
|
6504
|
+
required: ["sessionID", "mode"],
|
|
6505
|
+
additionalProperties: false
|
|
6506
|
+
},
|
|
6507
|
+
output: {
|
|
6508
|
+
type: "object",
|
|
6509
|
+
properties: {
|
|
6510
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6511
|
+
},
|
|
6512
|
+
required: ["mode"],
|
|
6513
|
+
additionalProperties: false
|
|
6514
|
+
}
|
|
6515
|
+
},
|
|
6516
|
+
get: {
|
|
6517
|
+
input: {
|
|
6518
|
+
type: "object",
|
|
6519
|
+
properties: { sessionID: { type: "string" } },
|
|
6520
|
+
required: ["sessionID"],
|
|
6521
|
+
additionalProperties: false
|
|
6522
|
+
},
|
|
6523
|
+
output: {
|
|
6524
|
+
type: "object",
|
|
6525
|
+
properties: {
|
|
6526
|
+
mode: { type: "string", enum: [...TRUST_MODES] }
|
|
6527
|
+
},
|
|
6528
|
+
required: ["mode"],
|
|
6529
|
+
additionalProperties: false
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
});
|
|
6534
|
+
function modePermissions(mode) {
|
|
6535
|
+
if (mode === "trusted") {
|
|
6536
|
+
return [
|
|
6537
|
+
{ action: "shell", resource: "*", effect: "allow" },
|
|
6538
|
+
{ action: "edit", resource: "*", effect: "allow" },
|
|
6539
|
+
{ action: "shell", resource: "git push --force*", effect: "deny" },
|
|
6540
|
+
{ action: "shell", resource: "git push -f*", effect: "deny" },
|
|
6541
|
+
{ action: "shell", resource: "git reset --hard*", effect: "deny" },
|
|
6542
|
+
{ action: "shell", resource: "git clean*", effect: "deny" },
|
|
6543
|
+
{ action: "shell", resource: "git filter-branch*", effect: "deny" },
|
|
6544
|
+
{ action: "shell", resource: "git filter-repo*", effect: "deny" },
|
|
6545
|
+
{ action: "shell", resource: "git rebase*", effect: "deny" },
|
|
6546
|
+
{ action: "shell", resource: "git checkout --*", effect: "deny" },
|
|
6547
|
+
{ action: "shell", resource: "git restore*", effect: "deny" }
|
|
6548
|
+
];
|
|
6549
|
+
}
|
|
6550
|
+
if (mode === "strict") {
|
|
6551
|
+
return [
|
|
6552
|
+
{ action: "shell", resource: "*", effect: "ask" },
|
|
6553
|
+
{ action: "edit", resource: "*", effect: "ask" }
|
|
6554
|
+
];
|
|
6555
|
+
}
|
|
6556
|
+
return [];
|
|
6557
|
+
}
|
|
6558
|
+
|
|
6357
6559
|
// src/file-lease-plugin.ts
|
|
6358
6560
|
var GVOZD_LEASE_TOOL = "gvozd_lease";
|
|
6359
6561
|
var GVOZD_CLAIM_TOOL = "gvozd_claim";
|
|
@@ -6426,7 +6628,9 @@ function enforceFileLeasePermission(event, config, manager) {
|
|
|
6426
6628
|
const role = roleOf(config, event.agent);
|
|
6427
6629
|
if (event.action === "edit") {
|
|
6428
6630
|
if (!role) {
|
|
6429
|
-
|
|
6631
|
+
if (event.agent && !manager.hasActiveLeases())
|
|
6632
|
+
return false;
|
|
6633
|
+
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
6634
|
}
|
|
6431
6635
|
if (role === "readonly")
|
|
6432
6636
|
return deny(event, `Agent ${event.agent} is readonly and cannot mutate project files`);
|
|
@@ -6440,9 +6644,15 @@ function enforceFileLeasePermission(event, config, manager) {
|
|
|
6440
6644
|
}
|
|
6441
6645
|
if (event.action !== "shell" && event.action !== "bash")
|
|
6442
6646
|
return false;
|
|
6443
|
-
if (role === "coordinator"
|
|
6647
|
+
if (role === "coordinator") {
|
|
6444
6648
|
return deny(event, `Agent ${event.agent} cannot use shell while file leases enforce structured mutations`);
|
|
6445
6649
|
}
|
|
6650
|
+
if (role === "writer" && safeReadonlyShell(event.agent, event.resources)) {
|
|
6651
|
+
return false;
|
|
6652
|
+
}
|
|
6653
|
+
if (role === "writer") {
|
|
6654
|
+
return deny(event, `Agent ${event.agent} cannot use shell beyond read-only verification while file leases enforce structured mutations`);
|
|
6655
|
+
}
|
|
6446
6656
|
if (manager.hasActiveLeases() && !(role === "readonly" && safeReadonlyShell(event.agent, event.resources))) {
|
|
6447
6657
|
return deny(event, "Shell commands are paused until all active writer file leases are released");
|
|
6448
6658
|
}
|
|
@@ -6706,6 +6916,55 @@ var src_default = Plugin.define({
|
|
|
6706
6916
|
try {
|
|
6707
6917
|
const fileLeases = await installFileLeaseRuntime(ctx, config, { caseInsensitive });
|
|
6708
6918
|
resources.push(fileLeases);
|
|
6919
|
+
if (ctx.rpc && typeof ctx.rpc.register === "function") {
|
|
6920
|
+
const sessionModes = new Map;
|
|
6921
|
+
const defaultMode = () => "balanced";
|
|
6922
|
+
const modeRpc = await ctx.rpc.register(GvozdMode, {
|
|
6923
|
+
set: async (raw) => {
|
|
6924
|
+
const input = raw;
|
|
6925
|
+
sessionModes.set(input.sessionID, input.mode);
|
|
6926
|
+
if (typeof ctx.permission.rules === "function") {
|
|
6927
|
+
await ctx.permission.rules({
|
|
6928
|
+
sessionID: input.sessionID,
|
|
6929
|
+
permissions: modePermissions(input.mode)
|
|
6930
|
+
});
|
|
6931
|
+
}
|
|
6932
|
+
return { mode: input.mode };
|
|
6933
|
+
},
|
|
6934
|
+
get: async (raw) => {
|
|
6935
|
+
const input = raw;
|
|
6936
|
+
return { mode: sessionModes.get(input.sessionID) ?? defaultMode() };
|
|
6937
|
+
}
|
|
6938
|
+
});
|
|
6939
|
+
resources.push(modeRpc);
|
|
6940
|
+
const leasesRpc = await ctx.rpc.register(GvozdLeases, {
|
|
6941
|
+
list: async () => {
|
|
6942
|
+
const output = {
|
|
6943
|
+
leases: fileLeases.manager.snapshot().map((lease) => ({
|
|
6944
|
+
leaseId: lease.leaseId,
|
|
6945
|
+
parentSessionID: lease.parentSessionID,
|
|
6946
|
+
...lease.sessionID ? { sessionID: lease.sessionID } : {},
|
|
6947
|
+
agent: lease.agent,
|
|
6948
|
+
label: lease.label,
|
|
6949
|
+
state: lease.state,
|
|
6950
|
+
files: [...lease.files],
|
|
6951
|
+
expiresAt: lease.expiresAt,
|
|
6952
|
+
lastActivityAt: lease.lastActivityAt
|
|
6953
|
+
}))
|
|
6954
|
+
};
|
|
6955
|
+
return output;
|
|
6956
|
+
}
|
|
6957
|
+
});
|
|
6958
|
+
resources.push(leasesRpc);
|
|
6959
|
+
const permissionRpc = await ctx.rpc.register(GvozdPermissions, {
|
|
6960
|
+
evaluate: async (raw) => {
|
|
6961
|
+
const input = raw;
|
|
6962
|
+
const agent = config.agents[input.agent];
|
|
6963
|
+
return evaluateInput(agent && !agent.disabled ? agent.permissions : undefined, input);
|
|
6964
|
+
}
|
|
6965
|
+
});
|
|
6966
|
+
resources.push(permissionRpc);
|
|
6967
|
+
}
|
|
6709
6968
|
const agentTransform = await ctx.agent.transform((agents) => {
|
|
6710
6969
|
applyAgentConfiguration(agents, config, models.data, mcpServers);
|
|
6711
6970
|
});
|