@pikaa-ai/pikaa 0.2.2 → 0.2.4
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/bin/pikaa.js +41 -30
- package/dist/cli.js +852 -112
- package/dist/index.js +54 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -152,6 +152,7 @@ class DefaultModelClientSession {
|
|
|
152
152
|
model,
|
|
153
153
|
messages,
|
|
154
154
|
stream: true,
|
|
155
|
+
stream_options: { include_usage: true },
|
|
155
156
|
temperature: params.temperature ?? 0.2
|
|
156
157
|
};
|
|
157
158
|
if (toolsPayload && toolsPayload.length > 0) {
|
|
@@ -200,6 +201,7 @@ class DefaultModelClientSession {
|
|
|
200
201
|
const reader = response.body.getReader();
|
|
201
202
|
const decoder = new TextDecoder;
|
|
202
203
|
let buffer = "";
|
|
204
|
+
let usageMetrics = {};
|
|
203
205
|
const pendingToolCalls = new Map;
|
|
204
206
|
try {
|
|
205
207
|
while (true) {
|
|
@@ -229,11 +231,23 @@ class DefaultModelClientSession {
|
|
|
229
231
|
};
|
|
230
232
|
}
|
|
231
233
|
pendingToolCalls.clear();
|
|
232
|
-
yield {
|
|
234
|
+
yield {
|
|
235
|
+
type: "done",
|
|
236
|
+
inputTokens: usageMetrics.inputTokens,
|
|
237
|
+
outputTokens: usageMetrics.outputTokens,
|
|
238
|
+
totalTokens: usageMetrics.totalTokens
|
|
239
|
+
};
|
|
233
240
|
return;
|
|
234
241
|
}
|
|
235
242
|
try {
|
|
236
243
|
const data = JSON.parse(dataStr);
|
|
244
|
+
if (data.usage) {
|
|
245
|
+
usageMetrics = {
|
|
246
|
+
inputTokens: data.usage.prompt_tokens,
|
|
247
|
+
outputTokens: data.usage.completion_tokens,
|
|
248
|
+
totalTokens: data.usage.total_tokens
|
|
249
|
+
};
|
|
250
|
+
}
|
|
237
251
|
const choice = data.choices?.[0];
|
|
238
252
|
if (!choice)
|
|
239
253
|
continue;
|
|
@@ -439,7 +453,7 @@ class ExecPolicy {
|
|
|
439
453
|
}
|
|
440
454
|
initDefaultRules() {
|
|
441
455
|
this.addRule(/^(git\s+(status|log|diff|branch|show|rev-parse))/i, "allow", "Safe git query");
|
|
442
|
-
this.addRule(/^(ls|dir|cat|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
|
|
456
|
+
this.addRule(/^(ls|dir|cat|type|grep|rg|find|pwd|echo|head|tail|wc|which|where)\b/i, "allow", "Safe read-only shell command");
|
|
443
457
|
this.addRule(/^(bun\s+(test|--version|-v)|npm\s+(test|--version|-v)|node\s+-v)\b/i, "allow", "Testing & runtime check");
|
|
444
458
|
this.addRule(/^(rm|del|rmdir|format|mkfs)\b/i, "prompt", "Destructive file removal");
|
|
445
459
|
this.addRule(/^(git\s+(push|reset\s+--hard|clean\s+-fd|rebase))\b/i, "prompt", "Destructive git operation");
|
|
@@ -489,7 +503,8 @@ function createShellTool(policy = new ExecPolicy) {
|
|
|
489
503
|
if (!command) {
|
|
490
504
|
return { output: "Error: 'command' argument cannot be empty", isError: true };
|
|
491
505
|
}
|
|
492
|
-
const
|
|
506
|
+
const activePolicy = ctx.execPolicy || policy;
|
|
507
|
+
const policyDecision = activePolicy.evaluate(command);
|
|
493
508
|
if (policyDecision.decision === "deny") {
|
|
494
509
|
return {
|
|
495
510
|
output: `Error: Command execution denied by policy: ${policyDecision.reason}`,
|
|
@@ -1423,12 +1438,16 @@ function buildSystemPrompt(params) {
|
|
|
1423
1438
|
const sections = [];
|
|
1424
1439
|
sections.push(params.basePrompt || "You are Groupy, an expert autonomous AI coding assistant. You think step-by-step, act surgically, and write clean, correct code.");
|
|
1425
1440
|
sections.push([
|
|
1426
|
-
"##
|
|
1427
|
-
"
|
|
1428
|
-
"
|
|
1429
|
-
"
|
|
1430
|
-
"
|
|
1431
|
-
"
|
|
1441
|
+
"## Editing Constraints & Guidelines",
|
|
1442
|
+
"- Use `apply_patch` for surgical single-file edits. TargetContent must match existing file content exactly.",
|
|
1443
|
+
"- Use `write_file` for creating new files or when completely replacing the full content of a file.",
|
|
1444
|
+
"- Use `read_file` to inspect files and `grep_search` / `find_files` to discover symbols and locate files across the project.",
|
|
1445
|
+
"- NEVER create temporary scripts, scratch files, or chunk files (e.g. `_tmp_*.ps1`, `_tmp_*.txt`, `split_*.py`) in the workspace to manipulate, split, or read files.",
|
|
1446
|
+
"- NEVER execute shell or PowerShell scripts as a workaround for reading, writing, or editing text files.",
|
|
1447
|
+
"- Use the `shell` tool ONLY for running tests, build targets, package installations, or checking environment/git status.",
|
|
1448
|
+
"- You may be in a dirty git worktree. NEVER revert existing changes made by the user.",
|
|
1449
|
+
"- NEVER use destructive commands like `git reset --hard` or `git checkout --`.",
|
|
1450
|
+
"- Be concise, direct, and act surgically. Write clean, correct code with minimal necessary modifications."
|
|
1432
1451
|
].join(`
|
|
1433
1452
|
`));
|
|
1434
1453
|
if (params.memoriesPrompt) {
|
|
@@ -1550,6 +1569,8 @@ async function runTurn(session, turnContext, input) {
|
|
|
1550
1569
|
skillsPrompt
|
|
1551
1570
|
});
|
|
1552
1571
|
let iteration = 0;
|
|
1572
|
+
let accumulatedInputTokens = 0;
|
|
1573
|
+
let accumulatedOutputTokens = 0;
|
|
1553
1574
|
const clientSession = session.modelClient.newSession();
|
|
1554
1575
|
try {
|
|
1555
1576
|
while (iteration < turnContext.maxIterations) {
|
|
@@ -1559,6 +1580,8 @@ async function runTurn(session, turnContext, input) {
|
|
|
1559
1580
|
iteration++;
|
|
1560
1581
|
let currentAgentText = "";
|
|
1561
1582
|
const toolCallRequests = [];
|
|
1583
|
+
let iterInputTokens = Math.ceil((effectiveSystemPrompt.length + JSON.stringify(session.getHistory()).length) / 4);
|
|
1584
|
+
let iterOutputTokens = 0;
|
|
1562
1585
|
const stream = clientSession.stream({
|
|
1563
1586
|
model: turnContext.model,
|
|
1564
1587
|
systemPrompt: effectiveSystemPrompt,
|
|
@@ -1585,10 +1608,20 @@ async function runTurn(session, turnContext, input) {
|
|
|
1585
1608
|
});
|
|
1586
1609
|
} else if (chunk.type === "tool_call") {
|
|
1587
1610
|
toolCallRequests.push(chunk);
|
|
1611
|
+
} else if (chunk.type === "done") {
|
|
1612
|
+
if (chunk.inputTokens !== undefined)
|
|
1613
|
+
iterInputTokens = chunk.inputTokens;
|
|
1614
|
+
if (chunk.outputTokens !== undefined)
|
|
1615
|
+
iterOutputTokens = chunk.outputTokens;
|
|
1588
1616
|
} else if (chunk.type === "error") {
|
|
1589
1617
|
throw chunk.error;
|
|
1590
1618
|
}
|
|
1591
1619
|
}
|
|
1620
|
+
if (iterOutputTokens === 0) {
|
|
1621
|
+
iterOutputTokens = Math.ceil((currentAgentText.length + JSON.stringify(toolCallRequests).length) / 4);
|
|
1622
|
+
}
|
|
1623
|
+
accumulatedInputTokens += iterInputTokens;
|
|
1624
|
+
accumulatedOutputTokens += iterOutputTokens;
|
|
1592
1625
|
if (currentAgentText.trim()) {
|
|
1593
1626
|
const agentItem = {
|
|
1594
1627
|
id: `msg_agent_${Date.now()}`,
|
|
@@ -1629,6 +1662,7 @@ async function runTurn(session, turnContext, input) {
|
|
|
1629
1662
|
cwd: turnContext.environment.cwd,
|
|
1630
1663
|
turnId,
|
|
1631
1664
|
signal,
|
|
1665
|
+
execPolicy: session.execPolicy,
|
|
1632
1666
|
requestApproval: async (description, command) => {
|
|
1633
1667
|
const approvalId = `appr_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
1634
1668
|
return session.requestApproval({
|
|
@@ -1666,9 +1700,16 @@ async function runTurn(session, turnContext, input) {
|
|
|
1666
1700
|
}
|
|
1667
1701
|
break;
|
|
1668
1702
|
}
|
|
1703
|
+
const totalContextTokens = estimateTotalTokens(session.getHistory()) + Math.ceil(effectiveSystemPrompt.length / 4);
|
|
1704
|
+
const maxContextTokens = 128000;
|
|
1669
1705
|
session.emitEvent({
|
|
1670
1706
|
type: "TurnCompleted",
|
|
1671
|
-
turnId
|
|
1707
|
+
turnId,
|
|
1708
|
+
inputTokens: accumulatedInputTokens,
|
|
1709
|
+
outputTokens: accumulatedOutputTokens,
|
|
1710
|
+
totalTokens: accumulatedInputTokens + accumulatedOutputTokens,
|
|
1711
|
+
contextTokens: totalContextTokens,
|
|
1712
|
+
maxContextTokens
|
|
1672
1713
|
});
|
|
1673
1714
|
} catch (error) {
|
|
1674
1715
|
const isAborted = error instanceof TurnAbortedError || signal.aborted;
|
|
@@ -1762,6 +1803,7 @@ class Session {
|
|
|
1762
1803
|
tools;
|
|
1763
1804
|
skillsLoader;
|
|
1764
1805
|
memoryStore;
|
|
1806
|
+
execPolicy;
|
|
1765
1807
|
history = [];
|
|
1766
1808
|
activeTurn = null;
|
|
1767
1809
|
status = "idle";
|
|
@@ -1779,6 +1821,7 @@ class Session {
|
|
|
1779
1821
|
this.tools = options.tools || new ToolRouter;
|
|
1780
1822
|
this.skillsLoader = options.skillsLoader;
|
|
1781
1823
|
this.memoryStore = options.memoryStore;
|
|
1824
|
+
this.execPolicy = options.execPolicy || new ExecPolicy;
|
|
1782
1825
|
this.history = options.initialHistory ? [...options.initialHistory] : [];
|
|
1783
1826
|
if (options.onEvent) {
|
|
1784
1827
|
this.eventListeners.push(options.onEvent);
|
|
@@ -3987,7 +4030,7 @@ class AuthClient {
|
|
|
3987
4030
|
</head>
|
|
3988
4031
|
<body>
|
|
3989
4032
|
<div class="box">
|
|
3990
|
-
<h1
|
|
4033
|
+
<h1>Authentication Successful!</h1>
|
|
3991
4034
|
<p>You have successfully logged in to Groupy CLI. You can close this window and return to your terminal.</p>
|
|
3992
4035
|
</div>
|
|
3993
4036
|
</body>
|