@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/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 { type: "done" };
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 policyDecision = policy.evaluate(command);
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
- "## Operational Guidelines",
1427
- "1. Think before acting. Understand the task and the code it touches before making edits.",
1428
- "2. Simplicity first: write the minimum code that solves the problem. No unnecessary abstractions.",
1429
- "3. Use `apply_patch` for surgical code replacements rather than overwriting whole files.",
1430
- "4. Execute shell commands with `shell` to inspect environment, run tests, or build targets.",
1431
- "5. Always verify your changes with tests or execution checks."
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>\u26A1 Authentication Successful!</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>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikaa-ai/pikaa",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "PIKAA CLI - AI coding agent that runs locally in your terminal.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",