@north-light/sdk 0.3.484 → 0.3.486

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 CHANGED
@@ -137,7 +137,7 @@ const {text, ended, error} = await client.runs.reply(run.run_id, run.message!).c
137
137
 
138
138
  A run working in the background reports what it is doing on `runs.get(runId).activity` (`starting`, `thinking`, `tool` with `tool.name` and `tool.summary`, `writing`, or `between_turns`; `null` once it is no longer `running`), so a polling page shows progress without reading `runs.trace`.
139
139
 
140
- `runs.listAll(filters)` and `memory.listAll(params)` (the person's knowledge and preference documents the caller can read: `public` by default, every privacy level with `crtr:memory:manage:user`) iterate every item across pages with `for await`, fetching the next page only as you go. `runs.delete(runId)` deletes a running run too — it stops the run's turns and sandboxes itself, so there is no need to `cancel` first; deleting a run that is already gone throws `NotFoundError`.
140
+ `runs.listAll(filters)` and `memory.listAll(params)` (the person's knowledge and preference documents the caller can read: `public` by default, `private` too with `crtr:memory:read_private:user`, every privacy level with `crtr:memory:manage:user`) iterate every item across pages with `for await`, fetching the next page only as you go. `runs.delete(runId)` deletes a running run too — it stops the run's turns and sandboxes itself, so there is no need to `cancel` first; deleting a run that is already gone throws `NotFoundError`.
141
141
 
142
142
  ## Read files and run bash
143
143
 
package/dist/client.d.ts CHANGED
@@ -124,7 +124,8 @@ export declare class Crouter {
124
124
  readonly shares: Shares;
125
125
  /** Questions agents in runs ask the user (app listener only). */
126
126
  readonly questions: Questions;
127
- /** Read the documents the person owns (`store: 'user'`): an app sees their `public` documents, and every privacy level with a `crtr:memory:manage:user` grant. */
127
+ /** Read the documents the person owns (`store: 'user'`): an app sees their `public` documents, `private` ones too while the grant holds
128
+ * `crtr:memory:read_private:user` (read-only; `friends` never), and every privacy level with a `crtr:memory:manage:user` grant. */
128
129
  readonly memory: Memory;
129
130
  /** Scope-filtered provider tools and daemon-mediated calls. */
130
131
  readonly providers: Providers;
package/dist/client.js CHANGED
@@ -63,7 +63,8 @@ export class Crouter {
63
63
  shares;
64
64
  /** Questions agents in runs ask the user (app listener only). */
65
65
  questions;
66
- /** Read the documents the person owns (`store: 'user'`): an app sees their `public` documents, and every privacy level with a `crtr:memory:manage:user` grant. */
66
+ /** Read the documents the person owns (`store: 'user'`): an app sees their `public` documents, `private` ones too while the grant holds
67
+ * `crtr:memory:read_private:user` (read-only; `friends` never), and every privacy level with a `crtr:memory:manage:user` grant. */
67
68
  memory;
68
69
  /** Scope-filtered provider tools and daemon-mediated calls. */
69
70
  providers;
@@ -148,6 +148,17 @@ export declare class OAuth2Client {
148
148
  * is absent, does not contain `{client_id}` exactly once, or does not expand to a URL on the issuer's origin.
149
149
  */
150
150
  accountAppUrl(): Promise<string>;
151
+ /**
152
+ * A link to the app's account page with one optional permission opened first: `accountAppUrl()` plus `permission=<scope>`
153
+ * and, when given, `return_to=<returnTo>`. The directory shows the switch for that scope and, once the person turns it
154
+ * on, a "Back to <app>" button to `returnTo` (accepted only on the origin of one of the app's registered redirect URIs;
155
+ * anything else is ignored). Async because it reads discovery as `accountAppUrl` does. Throws `CrouterError` when `scope`
156
+ * fails `parseScope`, and whatever `accountAppUrl` throws. The person's choice lands in the grant's `resolved_scopes`
157
+ * (read it with `grants.get`, follow it with `watchGrants`).
158
+ */
159
+ permissionUrl(scope: string, options?: {
160
+ returnTo?: string;
161
+ }): Promise<string>;
151
162
  /** Start a sign-in. When `scopes` includes `openid` the result carries the `nonce` to pass to the exchange; with literal scopes its type is `string`.
152
163
  * `prompt: 'consent'` makes the directory show its consent screen even when the person already approved these scopes. */
153
164
  authorizeUrl<const S extends readonly string[]>({ scopes, state, codeVerifier, prompt }: {
@@ -191,6 +191,27 @@ export class OAuth2Client {
191
191
  throw new CrouterError(`Directory discovery's account_app_uri_template is not a URL on ${origin}`);
192
192
  return expanded;
193
193
  }
194
+ /**
195
+ * A link to the app's account page with one optional permission opened first: `accountAppUrl()` plus `permission=<scope>`
196
+ * and, when given, `return_to=<returnTo>`. The directory shows the switch for that scope and, once the person turns it
197
+ * on, a "Back to <app>" button to `returnTo` (accepted only on the origin of one of the app's registered redirect URIs;
198
+ * anything else is ignored). Async because it reads discovery as `accountAppUrl` does. Throws `CrouterError` when `scope`
199
+ * fails `parseScope`, and whatever `accountAppUrl` throws. The person's choice lands in the grant's `resolved_scopes`
200
+ * (read it with `grants.get`, follow it with `watchGrants`).
201
+ */
202
+ async permissionUrl(scope, options = {}) {
203
+ try {
204
+ parseScope(scope);
205
+ }
206
+ catch {
207
+ throw new CrouterError(`Unsupported scope: ${scope}`);
208
+ }
209
+ const url = new URL(await this.accountAppUrl());
210
+ url.searchParams.set('permission', scope);
211
+ if (options.returnTo !== undefined)
212
+ url.searchParams.set('return_to', options.returnTo);
213
+ return url.toString();
214
+ }
194
215
  /** Start a sign-in. When `scopes` includes `openid` the result carries the `nonce` to pass to the exchange; with literal scopes its type is `string`.
195
216
  * `prompt: 'consent'` makes the directory show its consent screen even when the person already approved these scopes. */
196
217
  async authorizeUrl({ scopes, state, codeVerifier, prompt }) {
@@ -26,7 +26,7 @@ export interface MemoryListItem {
26
26
  kind: 'knowledge' | 'preference';
27
27
  preview: string;
28
28
  summary: string | null;
29
- /** Without a `crtr:memory:manage:user` grant an app only ever sees `public`. */
29
+ /** An app sees `public`; `private` too while its grant holds `crtr:memory:read_private:user`; `friends` only with `crtr:memory:manage:user`. */
30
30
  privacy: 'public' | 'friends' | 'private';
31
31
  /** The current revision number. */
32
32
  revision: number;
@@ -54,9 +54,11 @@ export interface MemoryDocument extends MemoryListItem {
54
54
  }
55
55
  /**
56
56
  * Reads the documents the person owns. The daemon filters each one by the caller's
57
- * grant: an app reads `public` documents with no scope, and every privacy level with
58
- * `crtr:memory:manage:user`; without it a `friends` or `private` document reads
59
- * exactly as an absent one (`NotFoundError`). Reading records nothing.
57
+ * grant: an app reads `public` documents with no scope, `private` ones too while the grant
58
+ * holds `crtr:memory:read_private:user` (the person may switch it on and off; `friends` never),
59
+ * and every privacy level with `crtr:memory:manage:user`; a document above the grant's level
60
+ * reads exactly as an absent one (`NotFoundError`). A run started with a `scopes` list sees
61
+ * `private` only if that list covers `crtr:memory:read_private:user` (names it, or `crtr:memory:manage:user`). Reading records nothing.
60
62
  */
61
63
  export declare class Memory {
62
64
  private readonly request;
@@ -1,9 +1,11 @@
1
1
  import { withQuery } from './request.js';
2
2
  /**
3
3
  * Reads the documents the person owns. The daemon filters each one by the caller's
4
- * grant: an app reads `public` documents with no scope, and every privacy level with
5
- * `crtr:memory:manage:user`; without it a `friends` or `private` document reads
6
- * exactly as an absent one (`NotFoundError`). Reading records nothing.
4
+ * grant: an app reads `public` documents with no scope, `private` ones too while the grant
5
+ * holds `crtr:memory:read_private:user` (the person may switch it on and off; `friends` never),
6
+ * and every privacy level with `crtr:memory:manage:user`; a document above the grant's level
7
+ * reads exactly as an absent one (`NotFoundError`). A run started with a `scopes` list sees
8
+ * `private` only if that list covers `crtr:memory:read_private:user` (names it, or `crtr:memory:manage:user`). Reading records nothing.
7
9
  */
8
10
  export class Memory {
9
11
  request;
@@ -2,7 +2,7 @@
2
2
  name: northlight
3
3
  description: "Use when building, debugging or shipping an app that signs people in with Northlight, starts runs on their Northlight runtime, or calls Northlight providers (sms, search, integrations, context, device) with @north-light/sdk."
4
4
  metadata:
5
- sdk-version: "0.3.484"
5
+ sdk-version: "0.3.486"
6
6
  ---
7
7
 
8
8
  # Building a Northlight app
@@ -11,7 +11,7 @@ An app on Northlight signs people in with their Northlight account, then starts
11
11
 
12
12
  ## First: check this skill matches the SDK
13
13
 
14
- This skill is for SDK version `0.3.484`. If the project has `node_modules/@north-light/sdk/package.json`, compare its `version` with that number. If they differ, run `npx @north-light/sdk skill` in the project, then read the rewritten `.agents/skills/northlight/SKILL.md` (or `.claude/skills/northlight/SKILL.md`) instead of this copy. That command needs no account and no crtr. A project that has no SDK yet starts with `npx @north-light/sdk@latest init <dir>`.
14
+ This skill is for SDK version `0.3.486`. If the project has `node_modules/@north-light/sdk/package.json`, compare its `version` with that number. If they differ, run `npx @north-light/sdk skill` in the project, then read the rewritten `.agents/skills/northlight/SKILL.md` (or `.claude/skills/northlight/SKILL.md`) instead of this copy. That command needs no account and no crtr. A project that has no SDK yet starts with `npx @north-light/sdk@latest init <dir>`.
15
15
 
16
16
  ## Where things run
17
17
 
@@ -298,6 +298,9 @@ export declare class FakeCloud {
298
298
  private runMessage;
299
299
  private question;
300
300
  private memoryStore;
301
+ /** `public` always; `private` with `crtr:memory:read_private:user`; every level with `crtr:memory:manage:user`; `friends` never
302
+ * below manage. Unlike the real runtime, which narrows a run to the `scopes` it was started with, this reads the user's whole
303
+ * grant (`user.grant.set`): narrowed runs are tested on the real runtime. */
301
304
  private memoryVisible;
302
305
  private memoryList;
303
306
  private providerEntry;
@@ -965,8 +965,14 @@ export class FakeCloud {
965
965
  if (value !== 'user')
966
966
  throw invalid('store must be "user"', 'store');
967
967
  }
968
+ /** `public` always; `private` with `crtr:memory:read_private:user`; every level with `crtr:memory:manage:user`; `friends` never
969
+ * below manage. Unlike the real runtime, which narrows a run to the `scopes` it was started with, this reads the user's whole
970
+ * grant (`user.grant.set`): narrowed runs are tested on the real runtime. */
968
971
  memoryVisible(user, doc) {
969
- return doc.privacy === 'public' || user.scopeStrings().some((scope) => covers(scope, 'crtr:memory:manage:user'));
972
+ const scopes = user.scopeStrings();
973
+ if (doc.privacy === 'public' || scopes.some((scope) => covers(scope, 'crtr:memory:manage:user')))
974
+ return true;
975
+ return doc.privacy === 'private' && scopes.some((scope) => covers(scope, 'crtr:memory:read_private:user'));
970
976
  }
971
977
  memoryList(user, query) {
972
978
  this.memoryStore(query);
package/dist/version.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /** This SDK's release version. Informational only: compatibility is the contract level, not this number. */
2
- export declare const SDK_VERSION = "0.3.484";
2
+ export declare const SDK_VERSION = "0.3.486";
3
3
  /** The release sent in `Crouter-Minimum-Runtime-Version` on every request. It is frozen, so a runtime from before the
4
4
  * contract level existed (which only knows this header) refuses this SDK, while every later runtime accepts it. */
5
5
  export declare const LEGACY_FLOOR = "0.3.423";
package/dist/version.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // SDK_VERSION is written by scripts/release-version.mjs on every release; do not edit by hand.
2
2
  /** This SDK's release version. Informational only: compatibility is the contract level, not this number. */
3
- export const SDK_VERSION = '0.3.484';
3
+ export const SDK_VERSION = '0.3.486';
4
4
  // LEGACY_FLOOR is written once by `scripts/contract.mjs release`, at the first release that carries the contract level
5
5
  // (the `0.0.0` placeholder is replaced there and never again); do not edit by hand.
6
6
  /** The release sent in `Crouter-Minimum-Runtime-Version` on every request. It is frozen, so a runtime from before the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@north-light/sdk",
3
- "version": "0.3.484",
4
- "crouterContract": "1.10",
3
+ "version": "0.3.486",
4
+ "crouterContract": "1.11",
5
5
  "description": "Typed Node and browser client for running crouter agents through the crtrd /v1 API.",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -44,8 +44,8 @@
44
44
  "prepublishOnly": "node -e \"if (process.env.npm_config_tag !== 'next') { console.error('@north-light/sdk publishes only with --tag next; scripts/promote-sdk.mjs is the only mover of latest.'); process.exit(1); }\""
45
45
  },
46
46
  "dependencies": {
47
- "@north-light/api": "0.3.484",
48
- "@north-light/identity": "0.3.484",
47
+ "@north-light/api": "0.3.486",
48
+ "@north-light/identity": "0.3.486",
49
49
  "jose": "^6.2.1"
50
50
  },
51
51
  "peerDependencies": {