@notis_ai/cli 0.2.0-beta.118.1 → 0.2.0-beta.121.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notis_ai/cli",
3
- "version": "0.2.0-beta.118.1",
3
+ "version": "0.2.0-beta.121.1",
4
4
  "description": "Agent-first Notis CLI for apps and generic tool execution",
5
5
  "type": "module",
6
6
  "bin": {
@@ -561,6 +561,14 @@ export async function ensureDevInstall({
561
561
  const skills = resolveConfiguredAppSkills(appConfig, projectDir);
562
562
  let linkedState = readLinkedState(projectDir);
563
563
  let linkedApp = null;
564
+ if (linkedState?.dev_app_id) {
565
+ const devApp = await getAccessibleApp(ctx.runtime, linkedState.dev_app_id, runTool);
566
+ if (!devApp || devApp.manifest?.is_dev !== true) {
567
+ const { dev_app_id: _devAppId, dev_linked_at: _devLinkedAt, ...rest } = linkedState;
568
+ linkedState = rest;
569
+ writeLinkedState(projectDir, linkedState);
570
+ }
571
+ }
564
572
  if (linkedState?.app_id) {
565
573
  linkedApp = await getAccessibleApp(ctx.runtime, linkedState.app_id, runTool);
566
574
  if (linkedApp?.manifest?.is_dev === true) {
@@ -631,7 +639,7 @@ async function getAccessibleApp(runtime, appId, runTool = runToolCommand) {
631
639
  const result = await runTool({
632
640
  runtime,
633
641
  toolName: GET_APP_TOOL,
634
- arguments_: { app_id: appId },
642
+ arguments_: { app_id: appId, include_documents: false },
635
643
  });
636
644
  if (result.payload?.app) {
637
645
  return {
@@ -640,7 +648,11 @@ async function getAccessibleApp(runtime, appId, runTool = runToolCommand) {
640
648
  };
641
649
  }
642
650
  const message = typeof result.payload?.message === 'string' ? result.payload.message : '';
643
- if (result.payload?.status === 'error' && /PGRST116|0 rows|no rows|not found/i.test(message)) {
651
+ const errorCode = result.payload?.code || result.payload?.error?.code;
652
+ if (
653
+ result.payload?.status === 'error'
654
+ && (errorCode === 'app_not_found' || /^App not found\.?$/i.test(message.trim()))
655
+ ) {
644
656
  return null;
645
657
  }
646
658
  throw usageError(`Could not verify access to app ${appId}${message ? `: ${message}` : '.'}`);
@@ -1,4 +1,4 @@
1
- import { createHash } from 'node:crypto';
1
+ import { createHash, randomUUID } from 'node:crypto';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
  import { usageError } from '../runtime/errors.js';
4
4
  import {
@@ -178,6 +178,11 @@ async function executeReadOnlySql(ctx, query, phase) {
178
178
  tools: [{ tool_slug: toolName, arguments: { query } }],
179
179
  },
180
180
  mutating: false,
181
+ // Always a fresh key, never the operator's --idempotency-key: a diagnostic
182
+ // must observe current state rather than replay a cached response, and two
183
+ // different queries under one reused key would collide on the request hash.
184
+ idempotencyKey: randomUUID(),
185
+ sendIdempotencyKeyWhenReading: true,
181
186
  });
182
187
  if (result.payload?.successful === false || result.payload?.error) {
183
188
  throw usageError(
@@ -56,12 +56,18 @@ export async function runToolCommand({
56
56
  mutating = false,
57
57
  idempotencyKey,
58
58
  fileBindings = [],
59
+ sendIdempotencyKeyWhenReading = false,
59
60
  }) {
61
+ // The server owns effect classification and requires a key whenever *its*
62
+ // metadata says write or unknown — a client-side `mutating: false` hint does
63
+ // not exempt the call. Callers that knowingly dispatch through an
64
+ // unknown-classified wrapper (e.g. COMPOSIO_MULTI_EXECUTE_TOOL) opt in so the
65
+ // request carries a key instead of being rejected as idempotency_key_required.
60
66
  const result = await callTool({
61
67
  runtime: { ...runtime, mutating },
62
68
  toolName,
63
69
  arguments_,
64
- idempotencyKey: mutating ? idempotencyKey : null,
70
+ idempotencyKey: mutating || sendIdempotencyKeyWhenReading ? idempotencyKey : null,
65
71
  fileBindings,
66
72
  });
67
73
  return result;