@notionhq/workers 0.3.0 → 0.4.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.
@@ -19,7 +19,7 @@ function createAiConnectorCapability(key, configuration, pacerDeclarations) {
19
19
  ...configuration.schedule === void 0 ? {} : { schedule: parseSchedule(configuration.schedule) }
20
20
  },
21
21
  async handler(runtimeContext, options) {
22
- const capabilityContext = createCapabilityContext();
22
+ const capabilityContext = createCapabilityContext("ai_connector");
23
23
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
24
24
  initPacerState(runtimeContext?.pacers, pacerDeclarations);
25
25
  let executionResult;
@@ -11,7 +11,7 @@ function createAutomationCapability(key, config) {
11
11
  },
12
12
  async handler(event, options) {
13
13
  try {
14
- const capabilityContext = createCapabilityContext();
14
+ const capabilityContext = createCapabilityContext("automation");
15
15
  await config.execute(event, capabilityContext);
16
16
  if (options?.concreteOutput) {
17
17
  return { status: "success" };
@@ -1,7 +1,8 @@
1
1
  import { Client } from "@notionhq/client";
2
+ import type { CapabilityType } from "../worker.js";
2
3
  export type CapabilityContext = {
3
4
  /** Notion API SDK client for this execution. */
4
5
  notion: Client;
5
6
  };
6
- export declare function createCapabilityContext(): CapabilityContext;
7
+ export declare function createCapabilityContext(capabilityType: CapabilityType): CapabilityContext;
7
8
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/capabilities/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,MAAM,iBAAiB,GAAG;IAC/B,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,uBAAuB,IAAI,iBAAiB,CAc3D"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/capabilities/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG;IAC/B,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAwCF,wBAAgB,uBAAuB,CACtC,cAAc,EAAE,cAAc,GAC5B,iBAAiB,CAenB"}
@@ -1,12 +1,30 @@
1
1
  import { Client } from "@notionhq/client";
2
- function createCapabilityContext() {
2
+ const FIX_STEPS = "\n\nTo fix this:\n1. Create an internal integration at https://www.notion.so/profile/integrations/internal\n2. Give the integration access to the relevant pages/databases\n3. Add NOTION_API_TOKEN=<your-token> to your .env file\n4. For deployed workers, run: ntn workers env push";
3
+ function missingTokenMessage(capabilityType) {
4
+ if (capabilityType === "tool") {
5
+ return "NOTION_API_TOKEN is not set. When tools are called through a Custom Agent, the platform sets NOTION_API_TOKEN automatically, using the permissions of the Custom Agent. Outside of that context (e.g. ntn workers exec), you must set it yourself." + FIX_STEPS;
6
+ }
7
+ return `NOTION_API_TOKEN is not set. context.notion requires an API token to make requests in ${capabilityType} capabilities to Notion.` + FIX_STEPS;
8
+ }
9
+ function createUnauthenticatedNotionProxy(capabilityType) {
10
+ return new Proxy({}, {
11
+ get(_target, prop) {
12
+ if (prop === "then" || typeof prop === "symbol") {
13
+ return void 0;
14
+ }
15
+ throw new Error(missingTokenMessage(capabilityType));
16
+ }
17
+ });
18
+ }
19
+ function createCapabilityContext(capabilityType) {
3
20
  const options = {};
4
21
  if (process.env.NOTION_API_BASE_URL) {
5
22
  options.baseUrl = process.env.NOTION_API_BASE_URL;
6
23
  }
7
- if (process.env.NOTION_API_TOKEN) {
8
- options.auth = process.env.NOTION_API_TOKEN;
24
+ if (!process.env.NOTION_API_TOKEN) {
25
+ return { notion: createUnauthenticatedNotionProxy(capabilityType) };
9
26
  }
27
+ options.auth = process.env.NOTION_API_TOKEN;
10
28
  const notion = new Client(options);
11
29
  return { notion };
12
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/capabilities/sync.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { ExecutionError } from "../error.js";
1
+ import { ExecutionError, RateLimitError } from "../error.js";
2
2
  import {
3
3
  getPacerState,
4
4
  initPacerState
@@ -19,7 +19,7 @@ function createSyncCapability(key, syncConfiguration, pacerDeclarations) {
19
19
  schedule: syncConfiguration.schedule ? parseSchedule(syncConfiguration.schedule) : void 0
20
20
  },
21
21
  async handler(runtimeContext, options) {
22
- const capabilityContext = createCapabilityContext();
22
+ const capabilityContext = createCapabilityContext("sync");
23
23
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
24
24
  initPacerState(runtimeContext?.pacers, pacerDeclarations);
25
25
  let executionResult;
@@ -29,12 +29,32 @@ function createSyncCapability(key, syncConfiguration, pacerDeclarations) {
29
29
  capabilityContext
30
30
  );
31
31
  } catch (err) {
32
+ if (err instanceof RateLimitError) {
33
+ if (!options?.concreteOutput) {
34
+ const envelope = {
35
+ _tag: "error",
36
+ error: {
37
+ _tag: "rate_limit",
38
+ name: err.name,
39
+ message: err.message,
40
+ ...err.retryAfter !== void 0 ? { retryAfter: err.retryAfter } : {}
41
+ }
42
+ };
43
+ writeOutput(envelope);
44
+ }
45
+ throw err;
46
+ }
32
47
  const error = new ExecutionError(err);
33
48
  if (!options?.concreteOutput) {
34
- writeOutput({
49
+ const envelope = {
35
50
  _tag: "error",
36
- error: { name: error.name, message: error.message }
37
- });
51
+ error: {
52
+ _tag: "generic",
53
+ name: error.name,
54
+ message: error.message
55
+ }
56
+ };
57
+ writeOutput(envelope);
38
58
  }
39
59
  throw error;
40
60
  }
@@ -1,11 +1,31 @@
1
1
  import type { SchemaBuilder } from "../schema-builder.js";
2
2
  import type { HandlerOptions, JSONValue } from "../types.js";
3
3
  import type { CapabilityContext } from "./context.js";
4
+ /**
5
+ * Hints describing a tool's behavior. Advisory metadata read by the platform
6
+ * (not the model) to decide whether a call is safe to auto-execute or
7
+ * requires user confirmation.
8
+ *
9
+ * Field names mirror the MCP tool annotation shape
10
+ * (`McpServerToolAnnotations`) so worker tools flow through the same
11
+ * confirmation / auto-execute machinery as MCP tools.
12
+ *
13
+ * Only hints with active consumers in the Notion client are included.
14
+ */
15
+ export interface ToolHints {
16
+ /**
17
+ * The tool only reads state and has no side effects. Safe to call
18
+ * repeatedly; safe to auto-execute under default policy. Tools without
19
+ * this hint are treated as write tools and prompt for confirmation.
20
+ */
21
+ readOnlyHint?: boolean;
22
+ }
4
23
  export interface ToolConfiguration<I extends JSONValue, O extends JSONValue = JSONValue> {
5
24
  title: string;
6
25
  description: string;
7
26
  schema: SchemaBuilder<I>;
8
27
  outputSchema?: SchemaBuilder<O>;
28
+ hints?: ToolHints;
9
29
  execute: (input: I, context: CapabilityContext) => O | Promise<O>;
10
30
  }
11
31
  /**
@@ -56,6 +76,7 @@ export declare function createToolCapability<I extends JSONValue, O extends JSON
56
76
  description: string;
57
77
  schema: import("../json-schema.js").JSONSchema<I>;
58
78
  outputSchema: import("../json-schema.js").JSONSchema<O> | undefined;
79
+ hints: ToolHints | undefined;
59
80
  };
60
81
  handler: {
61
82
  (input: JSONValue, options: HandlerOptions): Promise<O>;
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/capabilities/tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAkEtD,MAAM,WAAW,iBAAiB,CACjC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS;IAE/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED,MAAM,MAAM,cAAc,CACzB,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,IAC5B,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,EAC9B,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;;;;gBAiBf,SAAS,WAAW,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;gBAC/C,SAAS,GAAG,OAAO,CAC9C;YACA,IAAI,EAAE,SAAS,CAAC;YAChB,KAAK,EAAE,CAAC,CAAC;SACR,GACD;YACA,IAAI,EAAE,OAAO,CAAC;YACd,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;aAAE,CAAC;SACnE,CACH;;EA4GD"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/capabilities/tool.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAkEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAS;IACzB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB,CACjC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS;IAE/B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;IAK3B,MAAM;;;;;CAON;AAED,MAAM,MAAM,cAAc,CACzB,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,IAC5B,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CACnC,CAAC,SAAS,SAAS,EACnB,CAAC,SAAS,SAAS,GAAG,SAAS,EAC9B,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;;;;;gBAiBf,SAAS,WAAW,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;gBAC/C,SAAS,GAAG,OAAO,CAC9C;YACA,IAAI,EAAE,SAAS,CAAC;YAChB,KAAK,EAAE,CAAC,CAAC;SACR,GACD;YACA,IAAI,EAAE,OAAO,CAAC;YACd,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;aAAE,CAAC;SACnE,CACH;;EA6GD"}
@@ -114,7 +114,7 @@ function createToolCapability(key, config) {
114
114
  return result;
115
115
  }
116
116
  try {
117
- const capabilityContext = createCapabilityContext();
117
+ const capabilityContext = createCapabilityContext("tool");
118
118
  const result = await config.execute(input, capabilityContext);
119
119
  if (validateOutput && !validateOutput(result)) {
120
120
  const error = new InvalidToolOutputError(
@@ -164,7 +164,8 @@ function createToolCapability(key, config) {
164
164
  title: config.title,
165
165
  description: config.description,
166
166
  schema: inputSchema,
167
- outputSchema
167
+ outputSchema,
168
+ hints: config.hints
168
169
  },
169
170
  handler
170
171
  };
@@ -17,7 +17,7 @@ function createWebhookCapability(key, config) {
17
17
  },
18
18
  async handler(events, options) {
19
19
  try {
20
- const capabilityContext = createCapabilityContext();
20
+ const capabilityContext = createCapabilityContext("webhook");
21
21
  await config.execute(events, capabilityContext);
22
22
  if (options?.concreteOutput) {
23
23
  return { status: "success" };
package/dist/error.d.ts CHANGED
@@ -5,6 +5,42 @@ export declare class ExecutionError extends Error {
5
5
  readonly cause: unknown;
6
6
  constructor(cause: unknown);
7
7
  }
8
+ /**
9
+ * Throw this error from your sync execute handler to signal that the
10
+ * external API returned a rate-limit response (e.g. HTTP 429).
11
+ *
12
+ * When the platform receives this error it applies exponential backoff
13
+ * instead of retrying immediately. If `retryAfter` is provided, the
14
+ * platform will wait at least that many seconds before the next attempt.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * worker.sync("issues", {
19
+ * database: issuesDb,
20
+ * execute: async (state, ctx) => {
21
+ * const res = await fetch("https://api.example.com/issues");
22
+ * if (res.status === 429) {
23
+ * const retryAfter = Number(res.headers.get("Retry-After"));
24
+ * throw new RateLimitError({
25
+ * retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined,
26
+ * });
27
+ * }
28
+ * // ...
29
+ * },
30
+ * });
31
+ * ```
32
+ */
33
+ export declare class RateLimitError extends ExecutionError {
34
+ /**
35
+ * Optional number of seconds the external API asked us to wait
36
+ * before retrying. The platform takes the max of this value and its
37
+ * own exponential-backoff delay.
38
+ */
39
+ readonly retryAfter: number | undefined;
40
+ constructor(options?: {
41
+ retryAfter?: number;
42
+ });
43
+ }
8
44
  /**
9
45
  * Helper for exhaustive switch statements. TypeScript will error if a case is not handled.
10
46
  */
@@ -1 +1 @@
1
- {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAK1B;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C"}
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAK1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,cAAe,SAAQ,cAAc;IACjD;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE5B,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;CAK7C;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C"}
package/dist/error.js CHANGED
@@ -6,10 +6,24 @@ class ExecutionError extends Error {
6
6
  this.cause = cause;
7
7
  }
8
8
  }
9
+ class RateLimitError extends ExecutionError {
10
+ /**
11
+ * Optional number of seconds the external API asked us to wait
12
+ * before retrying. The platform takes the max of this value and its
13
+ * own exponential-backoff delay.
14
+ */
15
+ retryAfter;
16
+ constructor(options) {
17
+ super("Rate limited by external API");
18
+ this.name = "RateLimitError";
19
+ this.retryAfter = options?.retryAfter;
20
+ }
21
+ }
9
22
  function unreachable(value) {
10
23
  throw new Error(`Unreachable: ${value}`);
11
24
  }
12
25
  export {
13
26
  ExecutionError,
27
+ RateLimitError,
14
28
  unreachable
15
29
  };
package/dist/index.d.ts CHANGED
@@ -4,9 +4,10 @@ export type { AutomationCapability, AutomationConfiguration, AutomationEvent, Pa
4
4
  export type { CapabilityContext } from "./capabilities/context.js";
5
5
  export type { NotionManagedOAuthConfiguration, OAuthCapability, OAuthConfiguration, UserManagedOAuthConfiguration, } from "./capabilities/oauth.js";
6
6
  export type { SyncCapability, SyncChange, SyncChangeDelete, SyncChangeUpsert, SyncConfiguration, SyncExecutionResult, SyncMode, } from "./capabilities/sync.js";
7
- export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
7
+ export type { ToolCapability, ToolConfiguration, ToolHints, } from "./capabilities/tool.js";
8
8
  export type { WebhookCapability, WebhookConfiguration, WebhookEvent, } from "./capabilities/webhook.js";
9
9
  export { WebhookVerificationError } from "./capabilities/webhook.js";
10
+ export { RateLimitError } from "./error.js";
10
11
  export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
11
12
  export type { Infer, SchemaBuilder } from "./schema-builder.js";
12
13
  export { getSchema, j } from "./schema-builder.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChF,YAAY,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACX,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACX,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC1B,eAAe,EACf,uBAAuB,EACvB,wBAAwB,EACxB,4BAA4B,GAC5B,MAAM,gCAAgC,CAAC;AACxC,YAAY,EACX,oBAAoB,EACpB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,GAClB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,YAAY,EACX,+BAA+B,EAC/B,eAAe,EACf,kBAAkB,EAClB,6BAA6B,GAC7B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACX,cAAc,EACd,iBAAiB,EACjB,SAAS,GACT,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACX,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACZ,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAClE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EACX,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,WAAW,EACX,UAAU,EACV,QAAQ,GACR,MAAM,YAAY,CAAC;AACpB,YAAY,EACX,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { emojiIcon, imageIcon, notionIcon, place } from "./builder.js";
2
2
  import { WebhookVerificationError } from "./capabilities/webhook.js";
3
+ import { RateLimitError } from "./error.js";
3
4
  import { getSchema, j } from "./schema-builder.js";
4
5
  import { Worker } from "./worker.js";
5
6
  export {
7
+ RateLimitError,
6
8
  WebhookVerificationError,
7
9
  Worker,
8
10
  emojiIcon,
package/dist/worker.d.ts CHANGED
@@ -12,6 +12,7 @@ import type { HandlerOptions, JSONValue } from "./types.js";
12
12
  export type { AutomationConfiguration, AutomationEvent, AiConnectorConfiguration, CapabilityContext, OAuthConfiguration, NotionManagedOAuthConfiguration, UserManagedOAuthConfiguration, SyncConfiguration, ToolConfiguration, WebhookConfiguration, WebhookEvent, };
13
13
  export { WebhookVerificationError };
14
14
  type Capability = SyncCapability | AiConnectorCapability | ToolCapability<any, any> | AutomationCapability | OAuthCapability | WebhookCapability;
15
+ export type CapabilityType = Capability["_tag"];
15
16
  /**
16
17
  * Configuration for a database that Notion creates and manages on behalf
17
18
  * of the worker. The database is created on first deploy and its schema
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notionhq/workers",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "An SDK for building workers for the Notion Workers platform",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -70,7 +70,7 @@
70
70
  "vitest": "^4.0.8"
71
71
  },
72
72
  "peerDependencies": {
73
- "@notionhq/client": "^2.2.15"
73
+ "@notionhq/client": ">=2.2.15"
74
74
  },
75
75
  "dependencies": {
76
76
  "ajv": "^8.17.1",
@@ -154,7 +154,7 @@ export function createAiConnectorCapability<
154
154
  runtimeContext?: StatefulRuntimeContext<State>,
155
155
  options?: HandlerOptions,
156
156
  ) {
157
- const capabilityContext = createCapabilityContext();
157
+ const capabilityContext = createCapabilityContext("ai_connector");
158
158
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
159
159
 
160
160
  initPacerState(runtimeContext?.pacers, pacerDeclarations);
@@ -105,7 +105,7 @@ export function createAutomationCapability(
105
105
  options?: HandlerOptions,
106
106
  ): Promise<{ status: "success" } | undefined> {
107
107
  try {
108
- const capabilityContext = createCapabilityContext();
108
+ const capabilityContext = createCapabilityContext("automation");
109
109
  await config.execute(event, capabilityContext);
110
110
 
111
111
  if (options?.concreteOutput) {
@@ -1,22 +1,64 @@
1
1
  import { Client } from "@notionhq/client";
2
2
  import type { ClientOptions } from "@notionhq/client/build/src/Client.js";
3
+ import type { CapabilityType } from "../worker.js";
3
4
 
4
5
  export type CapabilityContext = {
5
6
  /** Notion API SDK client for this execution. */
6
7
  notion: Client;
7
8
  };
8
9
 
9
- export function createCapabilityContext(): CapabilityContext {
10
+ const FIX_STEPS =
11
+ "\n\nTo fix this:\n" +
12
+ "1. Create an internal integration at https://www.notion.so/profile/integrations/internal\n" +
13
+ "2. Give the integration access to the relevant pages/databases\n" +
14
+ "3. Add NOTION_API_TOKEN=<your-token> to your .env file\n" +
15
+ "4. For deployed workers, run: ntn workers env push";
16
+
17
+ function missingTokenMessage(capabilityType: CapabilityType): string {
18
+ if (capabilityType === "tool") {
19
+ return (
20
+ "NOTION_API_TOKEN is not set. " +
21
+ "When tools are called through a Custom Agent, the platform sets NOTION_API_TOKEN automatically, " +
22
+ "using the permissions of the Custom Agent. " +
23
+ "Outside of that context (e.g. ntn workers exec), you must set it yourself." +
24
+ FIX_STEPS
25
+ );
26
+ }
27
+
28
+ return (
29
+ "NOTION_API_TOKEN is not set. " +
30
+ `context.notion requires an API token to make requests in ${capabilityType} capabilities to Notion.` +
31
+ FIX_STEPS
32
+ );
33
+ }
34
+
35
+ function createUnauthenticatedNotionProxy(
36
+ capabilityType: CapabilityType,
37
+ ): Client {
38
+ return new Proxy({} as Client, {
39
+ get(_target, prop) {
40
+ if (prop === "then" || typeof prop === "symbol") {
41
+ return undefined;
42
+ }
43
+ throw new Error(missingTokenMessage(capabilityType));
44
+ },
45
+ });
46
+ }
47
+
48
+ export function createCapabilityContext(
49
+ capabilityType: CapabilityType,
50
+ ): CapabilityContext {
10
51
  const options: ClientOptions = {};
11
52
 
12
53
  if (process.env.NOTION_API_BASE_URL) {
13
54
  options.baseUrl = process.env.NOTION_API_BASE_URL;
14
55
  }
15
56
 
16
- if (process.env.NOTION_API_TOKEN) {
17
- options.auth = process.env.NOTION_API_TOKEN;
57
+ if (!process.env.NOTION_API_TOKEN) {
58
+ return { notion: createUnauthenticatedNotionProxy(capabilityType) };
18
59
  }
19
60
 
61
+ options.auth = process.env.NOTION_API_TOKEN;
20
62
  const notion = new Client(options);
21
63
 
22
64
  return { notion };
@@ -8,7 +8,7 @@ import {
8
8
  vi,
9
9
  } from "vitest";
10
10
  import * as Builder from "../builder.js";
11
- import { ExecutionError } from "../error.js";
11
+ import { ExecutionError, RateLimitError } from "../error.js";
12
12
  import { getPacerState } from "../pacer_internal.js";
13
13
  import * as Schema from "../schema.js";
14
14
  import { Worker } from "../worker.js";
@@ -251,6 +251,113 @@ describe("Worker.sync", () => {
251
251
  );
252
252
  });
253
253
 
254
+ it("wraps RateLimitError in error envelope with _tag rate_limit", async () => {
255
+ const worker = new Worker();
256
+ const db = worker.database("test", {
257
+ type: "managed",
258
+ initialTitle: "Test",
259
+ primaryKeyProperty: "ID",
260
+ schema: {
261
+ properties: {
262
+ ID: Schema.title(),
263
+ },
264
+ },
265
+ });
266
+
267
+ const capability = createSyncCapability("rateLimitSync", {
268
+ database: db,
269
+ execute: async () => {
270
+ throw new RateLimitError();
271
+ },
272
+ });
273
+
274
+ await expect(capability.handler()).rejects.toThrow(RateLimitError);
275
+
276
+ expect(stdoutSpy).toHaveBeenCalledWith(
277
+ expect.stringContaining(`"_tag":"rate_limit"`),
278
+ );
279
+ expect(stdoutSpy).toHaveBeenCalledWith(
280
+ expect.stringContaining(`"name":"RateLimitError"`),
281
+ );
282
+ });
283
+
284
+ it("includes retryAfter in rate_limit error envelope when provided", async () => {
285
+ const worker = new Worker();
286
+ const db = worker.database("test", {
287
+ type: "managed",
288
+ initialTitle: "Test",
289
+ primaryKeyProperty: "ID",
290
+ schema: {
291
+ properties: {
292
+ ID: Schema.title(),
293
+ },
294
+ },
295
+ });
296
+
297
+ const capability = createSyncCapability("rateLimitSync", {
298
+ database: db,
299
+ execute: async () => {
300
+ throw new RateLimitError({ retryAfter: 30 });
301
+ },
302
+ });
303
+
304
+ await expect(capability.handler()).rejects.toThrow(RateLimitError);
305
+
306
+ const output = stdoutSpy.mock.calls
307
+ .map((call) => String(call[0]))
308
+ .find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
309
+ const parsed = JSON.parse(
310
+ output.replace(
311
+ /\n<__notion_output__>(.*)<\/__notion_output__>\n/,
312
+ "$1",
313
+ ),
314
+ );
315
+ expect(parsed).toEqual({
316
+ _tag: "error",
317
+ error: {
318
+ _tag: "rate_limit",
319
+ name: "RateLimitError",
320
+ message:
321
+ "Error during worker execution: Rate limited by external API",
322
+ retryAfter: 30,
323
+ },
324
+ });
325
+ });
326
+
327
+ it("wraps RateLimitError without retryAfter correctly", async () => {
328
+ const worker = new Worker();
329
+ const db = worker.database("test", {
330
+ type: "managed",
331
+ initialTitle: "Test",
332
+ primaryKeyProperty: "ID",
333
+ schema: {
334
+ properties: {
335
+ ID: Schema.title(),
336
+ },
337
+ },
338
+ });
339
+
340
+ const capability = createSyncCapability("rateLimitSync", {
341
+ database: db,
342
+ execute: async () => {
343
+ throw new RateLimitError();
344
+ },
345
+ });
346
+
347
+ await expect(capability.handler()).rejects.toThrow(RateLimitError);
348
+
349
+ const output = stdoutSpy.mock.calls
350
+ .map((call) => String(call[0]))
351
+ .find((s) => s.includes(`"_tag":"rate_limit"`)) as string;
352
+ const parsed = JSON.parse(
353
+ output.replace(
354
+ /\n<__notion_output__>(.*)<\/__notion_output__>\n/,
355
+ "$1",
356
+ ),
357
+ );
358
+ expect(parsed.error.retryAfter).toBeUndefined();
359
+ });
360
+
254
361
  it("emits sync output with key and default targetDatabaseKey", async () => {
255
362
  const worker = new Worker();
256
363
  const tasks = worker.database("tasks", {
@@ -1,4 +1,4 @@
1
- import { ExecutionError } from "../error.js";
1
+ import { ExecutionError, RateLimitError } from "../error.js";
2
2
  import {
3
3
  getPacerState,
4
4
  initPacerState,
@@ -28,6 +28,18 @@ import {
28
28
  type RuntimeContext as StatefulRuntimeContext,
29
29
  } from "./stateful-capability.js";
30
30
 
31
+ type SyncErrorEnvelope =
32
+ | { _tag: "error"; error: { _tag: "generic"; name: string; message: string } }
33
+ | {
34
+ _tag: "error";
35
+ error: {
36
+ _tag: "rate_limit";
37
+ name: string;
38
+ message: string;
39
+ retryAfter?: number;
40
+ };
41
+ };
42
+
31
43
  /**
32
44
  * Maps a property configuration to its corresponding value type.
33
45
  */
@@ -239,7 +251,7 @@ export function createSyncCapability<
239
251
  runtimeContext?: StatefulRuntimeContext<Context>,
240
252
  options?: HandlerOptions,
241
253
  ) {
242
- const capabilityContext = createCapabilityContext();
254
+ const capabilityContext = createCapabilityContext("sync");
243
255
  const state = runtimeContext?.state ?? runtimeContext?.userContext;
244
256
 
245
257
  initPacerState(runtimeContext?.pacers, pacerDeclarations);
@@ -251,12 +263,35 @@ export function createSyncCapability<
251
263
  capabilityContext,
252
264
  );
253
265
  } catch (err) {
266
+ if (err instanceof RateLimitError) {
267
+ if (!options?.concreteOutput) {
268
+ const envelope: SyncErrorEnvelope = {
269
+ _tag: "error",
270
+ error: {
271
+ _tag: "rate_limit",
272
+ name: err.name,
273
+ message: err.message,
274
+ ...(err.retryAfter !== undefined
275
+ ? { retryAfter: err.retryAfter }
276
+ : {}),
277
+ },
278
+ };
279
+ writeOutput(envelope);
280
+ }
281
+ throw err;
282
+ }
283
+
254
284
  const error = new ExecutionError(err);
255
285
  if (!options?.concreteOutput) {
256
- writeOutput({
286
+ const envelope: SyncErrorEnvelope = {
257
287
  _tag: "error",
258
- error: { name: error.name, message: error.message },
259
- });
288
+ error: {
289
+ _tag: "generic",
290
+ name: error.name,
291
+ message: error.message,
292
+ },
293
+ };
294
+ writeOutput(envelope);
260
295
  }
261
296
  throw error;
262
297
  }
@@ -222,6 +222,69 @@ describe("Worker.tool", () => {
222
222
  expect(capability.config.schema).toBeDefined();
223
223
  });
224
224
 
225
+ describe("hints", () => {
226
+ it("omits hints from config when not specified (existing tools unchanged)", () => {
227
+ const capability = createToolCapability<{ name: string }, string>(
228
+ "noHints",
229
+ {
230
+ title: "No Hints",
231
+ description: "A tool without hints",
232
+ schema: j.object({ name: j.string() }),
233
+ execute: ({ name }) => `Hello, ${name}`,
234
+ },
235
+ );
236
+
237
+ expect(capability.config.hints).toBeUndefined();
238
+ });
239
+
240
+ it("flows readOnlyHint=true through to config", () => {
241
+ const capability = createToolCapability<{ query: string }, string>(
242
+ "readOnly",
243
+ {
244
+ title: "Read Only",
245
+ description: "A read-only tool",
246
+ schema: j.object({ query: j.string() }),
247
+ hints: { readOnlyHint: true },
248
+ execute: ({ query }) => query,
249
+ },
250
+ );
251
+
252
+ expect(capability.config.hints).toEqual({ readOnlyHint: true });
253
+ });
254
+
255
+ it("flows readOnlyHint=false through to config", () => {
256
+ const capability = createToolCapability<{ pageId: string }, string>(
257
+ "writeTool",
258
+ {
259
+ title: "Write Tool",
260
+ description: "A write tool",
261
+ schema: j.object({ pageId: j.string() }),
262
+ hints: { readOnlyHint: false },
263
+ execute: ({ pageId }) => pageId,
264
+ },
265
+ );
266
+
267
+ expect(capability.config.hints).toEqual({ readOnlyHint: false });
268
+ });
269
+
270
+ it("does not affect execution behavior", async () => {
271
+ const capability = createToolCapability<{ x: number }, number>(
272
+ "doubleReadOnly",
273
+ {
274
+ title: "Double",
275
+ description: "Doubles a number",
276
+ schema: j.object({ x: j.number() }),
277
+ hints: { readOnlyHint: true },
278
+ execute: ({ x }) => x * 2,
279
+ },
280
+ );
281
+
282
+ const result = await capability.handler({ x: 7 });
283
+
284
+ expect(result).toEqual({ _tag: "success", value: 14 });
285
+ });
286
+ });
287
+
225
288
  it("invalid output with custom output schema", async () => {
226
289
  const capability = createToolCapability<
227
290
  { value: number },
@@ -70,6 +70,26 @@ function validateRequiredProperties(
70
70
  }
71
71
  }
72
72
 
73
+ /**
74
+ * Hints describing a tool's behavior. Advisory metadata read by the platform
75
+ * (not the model) to decide whether a call is safe to auto-execute or
76
+ * requires user confirmation.
77
+ *
78
+ * Field names mirror the MCP tool annotation shape
79
+ * (`McpServerToolAnnotations`) so worker tools flow through the same
80
+ * confirmation / auto-execute machinery as MCP tools.
81
+ *
82
+ * Only hints with active consumers in the Notion client are included.
83
+ */
84
+ export interface ToolHints {
85
+ /**
86
+ * The tool only reads state and has no side effects. Safe to call
87
+ * repeatedly; safe to auto-execute under default policy. Tools without
88
+ * this hint are treated as write tools and prompt for confirmation.
89
+ */
90
+ readOnlyHint?: boolean;
91
+ }
92
+
73
93
  export interface ToolConfiguration<
74
94
  I extends JSONValue,
75
95
  O extends JSONValue = JSONValue,
@@ -78,6 +98,7 @@ export interface ToolConfiguration<
78
98
  description: string;
79
99
  schema: SchemaBuilder<I>;
80
100
  outputSchema?: SchemaBuilder<O>;
101
+ hints?: ToolHints;
81
102
  execute: (input: I, context: CapabilityContext) => O | Promise<O>;
82
103
  }
83
104
 
@@ -218,7 +239,7 @@ export function createToolCapability<
218
239
  }
219
240
 
220
241
  try {
221
- const capabilityContext = createCapabilityContext();
242
+ const capabilityContext = createCapabilityContext("tool");
222
243
  const result = await config.execute(input, capabilityContext);
223
244
  if (validateOutput && !validateOutput(result)) {
224
245
  const error = new InvalidToolOutputError(
@@ -281,6 +302,7 @@ export function createToolCapability<
281
302
  description: config.description,
282
303
  schema: inputSchema,
283
304
  outputSchema: outputSchema,
305
+ hints: config.hints,
284
306
  },
285
307
  handler,
286
308
  };
@@ -118,7 +118,7 @@ export function createWebhookCapability(
118
118
  options?: HandlerOptions,
119
119
  ): Promise<{ status: "success" } | undefined> {
120
120
  try {
121
- const capabilityContext = createCapabilityContext();
121
+ const capabilityContext = createCapabilityContext("webhook");
122
122
  await config.execute(events, capabilityContext);
123
123
 
124
124
  if (options?.concreteOutput) {
package/src/error.ts CHANGED
@@ -11,6 +11,46 @@ export class ExecutionError extends Error {
11
11
  }
12
12
  }
13
13
 
14
+ /**
15
+ * Throw this error from your sync execute handler to signal that the
16
+ * external API returned a rate-limit response (e.g. HTTP 429).
17
+ *
18
+ * When the platform receives this error it applies exponential backoff
19
+ * instead of retrying immediately. If `retryAfter` is provided, the
20
+ * platform will wait at least that many seconds before the next attempt.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * worker.sync("issues", {
25
+ * database: issuesDb,
26
+ * execute: async (state, ctx) => {
27
+ * const res = await fetch("https://api.example.com/issues");
28
+ * if (res.status === 429) {
29
+ * const retryAfter = Number(res.headers.get("Retry-After"));
30
+ * throw new RateLimitError({
31
+ * retryAfter: Number.isFinite(retryAfter) ? retryAfter : undefined,
32
+ * });
33
+ * }
34
+ * // ...
35
+ * },
36
+ * });
37
+ * ```
38
+ */
39
+ export class RateLimitError extends ExecutionError {
40
+ /**
41
+ * Optional number of seconds the external API asked us to wait
42
+ * before retrying. The platform takes the max of this value and its
43
+ * own exponential-backoff delay.
44
+ */
45
+ readonly retryAfter: number | undefined;
46
+
47
+ constructor(options?: { retryAfter?: number }) {
48
+ super("Rate limited by external API");
49
+ this.name = "RateLimitError";
50
+ this.retryAfter = options?.retryAfter;
51
+ }
52
+ }
53
+
14
54
  /**
15
55
  * Helper for exhaustive switch statements. TypeScript will error if a case is not handled.
16
56
  */
package/src/index.ts CHANGED
@@ -37,13 +37,18 @@ export type {
37
37
  SyncExecutionResult,
38
38
  SyncMode,
39
39
  } from "./capabilities/sync.js";
40
- export type { ToolCapability, ToolConfiguration } from "./capabilities/tool.js";
40
+ export type {
41
+ ToolCapability,
42
+ ToolConfiguration,
43
+ ToolHints,
44
+ } from "./capabilities/tool.js";
41
45
  export type {
42
46
  WebhookCapability,
43
47
  WebhookConfiguration,
44
48
  WebhookEvent,
45
49
  } from "./capabilities/webhook.js";
46
50
  export { WebhookVerificationError } from "./capabilities/webhook.js";
51
+ export { RateLimitError } from "./error.js";
47
52
  export type { AnyJSONSchema, JSONSchema } from "./json-schema.js";
48
53
  export type { Infer, SchemaBuilder } from "./schema-builder.js";
49
54
  export { getSchema, j } from "./schema-builder.js";
package/src/worker.ts CHANGED
@@ -65,6 +65,8 @@ type Capability =
65
65
  | OAuthCapability
66
66
  | WebhookCapability;
67
67
 
68
+ export type CapabilityType = Capability["_tag"];
69
+
68
70
  /**
69
71
  * Configuration for a database that Notion creates and manages on behalf
70
72
  * of the worker. The database is created on first deploy and its schema