@kindgi/sdk 0.1.0 → 0.1.2

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
@@ -54,10 +54,7 @@ point for JSON coming off disk / network.
54
54
  import { createClient } from '@kindgi/sdk/client';
55
55
  import type { AgentId } from '@kindgi/sdk/types';
56
56
 
57
- const client = createClient({
58
- apiUrl: process.env.KINDGI_API_URL!,
59
- auth: { kind: 'apiToken', token: process.env.KINDGI_API_TOKEN! },
60
- });
57
+ const client = createClient();
61
58
 
62
59
  const run = await client.runs.start({
63
60
  agent: 'acme.drafting' as AgentId,
@@ -65,9 +62,32 @@ const run = await client.runs.start({
65
62
  });
66
63
  ```
67
64
 
68
- Re-exports:
65
+ `createClient()` finds the runtime by itself. Every option is optional:
66
+
67
+ - `apiUrl` and `auth` come from `KINDGI_API_URL` and `KINDGI_API_TOKEN`;
68
+ - in development, when those aren't set, from the running `kindgi dev`
69
+ (the nearest `.kindgirc.json`), with a one-time warning to put them in
70
+ your env file (`.env` / `.env.local`);
71
+ - a token that doesn't match the running `kindgi dev`'s for the same URL
72
+ (after `kindgi dev --reset`) is warned about once.
73
+
74
+ Production (`NODE_ENV` or `KINDGI_ENV` = `production`) never reads
75
+ `.kindgirc.json`: the env or explicit options must say. Explicit options
76
+ always win, field by field:
77
+
78
+ ```ts
79
+ const client = createClient({
80
+ apiUrl: 'https://kindgi.internal.acme.com',
81
+ auth: { kind: 'apiToken', token: await secrets.get('kindgi-api-token') },
82
+ });
83
+ ```
84
+
85
+ In a browser, pass `apiUrl` and `auth` to `@kindgi/client`'s `createClient`
86
+ (the explicit client underneath).
87
+
88
+ Exports:
69
89
 
70
- - `createClient`, `KindgiClient`.
90
+ - `createClient` (above), `KindgiClient`.
71
91
  - Every resource client type — `AgentsClient`, `RunsClient`, `ToolsClient`,
72
92
  `ApprovalsClient`, `SupervisorClient`, `ProposalsClient`, `ObservationsClient`,
73
93
  `FlowsClient`, `GuardrailsClient`, `ConversationsClient`, `MemoryClient`,
package/dist/client.d.ts CHANGED
@@ -1,23 +1,28 @@
1
+ import type { ClientOptions, KindgiClient } from '@kindgi/client';
2
+ export type { KindgiClient } from '@kindgi/client';
1
3
  /**
2
- * `@kindgi/sdk/client` — client callsite surface.
4
+ * A client for the app's Kindgi runtime. Every option is optional:
3
5
  *
4
- * Re-exports `createClient` plus every resource-client type, the error
5
- * surface, transport primitives, and streaming helpers from the
6
- * `@kindgi/client` package (`sdks/typescript/` in this repository).
7
- * Names preserved verbatim.
6
+ * - `apiUrl` and `auth` come from `KINDGI_API_URL` and `KINDGI_API_TOKEN`;
7
+ * - outside production, when those aren't set, from the running
8
+ * `kindgi dev` (the nearest `.kindgirc.json`), with a one-time warning to
9
+ * put them in the app's env file;
10
+ * - a token that doesn't match the running `kindgi dev`'s for the same
11
+ * `apiUrl` (after `kindgi dev --reset`) is warned about once.
8
12
  *
9
- * This sub-path intentionally does NOT re-export the branded ID types
10
- * (`AgentId`, `RunId`, etc.) that `@kindgi/client` also happens to
11
- * surface — those live in `@kindgi/sdk/types` to keep the flat barrel
12
- * (`import { ... } from '@kindgi/sdk'`) collision-free.
13
+ * Production (`NODE_ENV` or `KINDGI_ENV` = `production`) never reads
14
+ * `.kindgirc.json`: there, the env or explicit options must say. In a
15
+ * browser, pass `apiUrl` and `auth`. `@kindgi/client`'s `createClient` is
16
+ * the explicit form underneath.
13
17
  *
14
- * Callsite examples target
15
- * `import { createClient } from '@kindgi/sdk/client'`.
18
+ * @example
19
+ * ```ts
20
+ * import { createClient } from '@kindgi/sdk/client';
16
21
  *
17
- * @module @kindgi/sdk/client
22
+ * const kindgi = createClient();
23
+ * ```
18
24
  */
19
- export { createClient } from '@kindgi/client';
20
- export type { KindgiClient } from '@kindgi/client';
25
+ export declare function createClient(options?: Partial<ClientOptions>): KindgiClient;
21
26
  export type { AdapterConfigureInput, AdapterFilter, AdapterTestInput, AdaptersClient, AddMembershipInput, AgentsClient, ApplyProposalInput, ApprovalFilter, ApprovalsClient, ArtifactFilter, ArtifactsClient, AuditClient, AuditExportInput, BatchDecideInput, BudgetsClient, CapabilitiesClient, CapabilityFilter, ClientOptions, CompleteTokenInput, ConversationFilter, ConversationsClient, CostClient, DecideInput, DraftProposalsInput, EventsClient, FactsClient, FlowFilter, FlowsClient, FlowValidateResult, GuardrailFilter, GuardrailsClient, LogsClient, McpClient, McpEndpointFilter, McpEndpointsClient, TeamMembershipsClient, MemoryClient, MessageFilter, ObservationFilter, ObservationsClient, OrgsClient, PackFilter, PacksClient, PageFilter, PatternsInput, PoliciesClient, PolicyListFilter, PolicyVersionsFilter, ProposalDryRunInput, ProposalListInput, ProposalsClient, ProvenanceClient, ProvenanceExportInput, ProviderFilter, ProvidersClient, ReflectReviewInput, RegistryClient, RegistryFilter, ResumeRunInput, ReviewerFilter, ReviewersClient, RollbackProposalInput, Run, RunsClient, SchedulesClient, SessionsClient, StartRunInput, SubmitForReviewInput, SubscriptionsClient, SupervisorClient, ListTeamsFilter, TeamsClient, TenantClient, TenantConfigClient, TokenFilter, TokensClient, ReinstateToolVersionResult, ToolFilter, ToolVersionFilter, ToolsClient, Transport, TransportRequest, UsageClient, UsageSummaryInput, UserFilter, UsersClient, WebhooksClient, WithdrawProposalInput, } from '@kindgi/client';
22
27
  export { KindgiApiError, fromWire, notImplementedInPreview, notYetWired } from '@kindgi/client';
23
28
  export type { AuthError, ConflictError, GuardrailViolation, GuardrailViolationError, InvalidRequestError, NetworkError, NotFoundError, NotImplementedInPreviewError, NotYetWiredError, RateLimitedError, ServerError, KindgiError, } from '@kindgi/client';
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,YAAY,EACV,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,GAAG,EACH,UAAU,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,0BAA0B,EAC1B,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,cAAc,EACd,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChG,YAAY,EACV,SAAS,EACT,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIlE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,YAAY,CAE/E;AAGD,YAAY,EACV,qBAAqB,EACrB,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,GAAG,EACH,UAAU,EACV,eAAe,EACf,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,0BAA0B,EAC1B,UAAU,EACV,iBAAiB,EACjB,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,cAAc,EACd,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChG,YAAY,EACV,SAAS,EACT,aAAa,EACb,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,4BAA4B,EAC5B,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC3D,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC"}
package/dist/client.js CHANGED
@@ -19,7 +19,33 @@
19
19
  * @module @kindgi/sdk/client
20
20
  */
21
21
  // ---- Client factory + top-level shape ----
22
- export { createClient } from '@kindgi/client';
22
+ import { createClient as createKindgiClient } from '@kindgi/client';
23
+ import { resolveClientOptions } from './runtime-config.js';
24
+ /**
25
+ * A client for the app's Kindgi runtime. Every option is optional:
26
+ *
27
+ * - `apiUrl` and `auth` come from `KINDGI_API_URL` and `KINDGI_API_TOKEN`;
28
+ * - outside production, when those aren't set, from the running
29
+ * `kindgi dev` (the nearest `.kindgirc.json`), with a one-time warning to
30
+ * put them in the app's env file;
31
+ * - a token that doesn't match the running `kindgi dev`'s for the same
32
+ * `apiUrl` (after `kindgi dev --reset`) is warned about once.
33
+ *
34
+ * Production (`NODE_ENV` or `KINDGI_ENV` = `production`) never reads
35
+ * `.kindgirc.json`: there, the env or explicit options must say. In a
36
+ * browser, pass `apiUrl` and `auth`. `@kindgi/client`'s `createClient` is
37
+ * the explicit form underneath.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { createClient } from '@kindgi/sdk/client';
42
+ *
43
+ * const kindgi = createClient();
44
+ * ```
45
+ */
46
+ export function createClient(options = {}) {
47
+ return createKindgiClient(resolveClientOptions(options));
48
+ }
23
49
  // ---- Error surface ----
24
50
  export { KindgiApiError, fromWire, notImplementedInPreview, notYetWired } from '@kindgi/client';
25
51
  // ---- Streaming helpers (SSE parser + resume) ----
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AAEH,6CAA6C;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AA+F9C,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAgBhG,oDAAoD;AACpD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGxD,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,iCAAiC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AAEH,6CAA6C;AAC7C,OAAO,EAAE,YAAY,IAAI,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAI3D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkC,EAAE;IAC/D,OAAO,kBAAkB,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AAC3D,CAAC;AA8FD,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAgBhG,oDAAoD;AACpD,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAGxD,qFAAqF;AACrF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Where an app's client finds its Kindgi runtime when `createClient` is
3
+ * called without `apiUrl` / `auth`:
4
+ *
5
+ * 1. `KINDGI_API_URL` and `KINDGI_API_TOKEN` from the environment;
6
+ * 2. outside production, the running `kindgi dev`, from the nearest
7
+ * `.kindgirc.json` at or above the working directory (it records the
8
+ * runtime's `apiUrl` and the dev `token`), with a one-time warning to
9
+ * put them in the app's env file;
10
+ * 3. otherwise, an error that says what to set.
11
+ *
12
+ * Production (`NODE_ENV` or `KINDGI_ENV` set to `production`) never reads
13
+ * `.kindgirc.json`. Whatever the source, a token that differs from the
14
+ * running `kindgi dev`'s for the same `apiUrl` (the stale token after
15
+ * `kindgi dev --reset`) is warned about once.
16
+ *
17
+ * Node only: the file is read through `process.getBuiltinModule`, never a
18
+ * static `node:` import, so the module stays safe to bundle for browsers,
19
+ * where only explicit options apply.
20
+ */
21
+ import type { ClientOptions } from '@kindgi/client';
22
+ /** The file `kindgi dev` writes in the pack directory. */
23
+ export declare const DEV_RUNTIME_FILE = ".kindgirc.json";
24
+ /** What a client needs from a running `kindgi dev`. */
25
+ export interface DevRuntime {
26
+ readonly apiUrl: string;
27
+ readonly token: string;
28
+ /** The `.kindgirc.json` it came from. */
29
+ readonly path: string;
30
+ }
31
+ /** Where the lookup runs: the process's own environment by default (tests pass their own). */
32
+ export interface RuntimeConfigContext {
33
+ readonly env: Readonly<Record<string, string | undefined>>;
34
+ /** The directory the `.kindgirc.json` search starts from. */
35
+ readonly cwd: string | undefined;
36
+ readonly warn: (message: string) => void;
37
+ }
38
+ export declare function defaultContext(): RuntimeConfigContext;
39
+ export declare function isProduction(env: RuntimeConfigContext['env']): boolean;
40
+ /**
41
+ * The client options with every missing field resolved (see the module
42
+ * comment). Throws when no `apiUrl` or no token can be found.
43
+ */
44
+ export declare function resolveClientOptions(options: Partial<ClientOptions>, context?: RuntimeConfigContext): ClientOptions;
45
+ /**
46
+ * The running `kindgi dev`'s `apiUrl` and `token`, from the nearest
47
+ * `.kindgirc.json` at or above `from`; `undefined` when there's none, it
48
+ * can't be read, or the runtime can't read files (a browser).
49
+ */
50
+ export declare function findDevRuntime(from: string | undefined): DevRuntime | undefined;
51
+ /** Tests only: forget which warnings were printed. */
52
+ export declare function resetWarningsForTests(): void;
53
+ //# sourceMappingURL=runtime-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.d.ts","sourceRoot":"","sources":["../src/runtime-config.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAc,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEhE,0DAA0D;AAC1D,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AAEjD,uDAAuD;AACvD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,8FAA8F;AAC9F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC3D,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C;AAYD,wBAAgB,cAAc,IAAI,oBAAoB,CAOrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,oBAAoB,CAAC,KAAK,CAAC,GAAG,OAAO,CAEtE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,EAC/B,OAAO,GAAE,oBAAuC,GAC/C,aAAa,CAqDf;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAU/E;AA+BD,sDAAsD;AACtD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C"}
@@ -0,0 +1,114 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /** The file `kindgi dev` writes in the pack directory. */
4
+ export const DEV_RUNTIME_FILE = '.kindgirc.json';
5
+ const ENV_FILE_HINT = 'your env file (.env / .env.local)';
6
+ const warned = new Set();
7
+ function warnOnce(context, key, message) {
8
+ if (warned.has(key))
9
+ return;
10
+ warned.add(key);
11
+ context.warn(`[kindgi] ${message}`);
12
+ }
13
+ export function defaultContext() {
14
+ const proc = typeof process === 'undefined' ? undefined : process;
15
+ return {
16
+ env: proc?.env ?? {},
17
+ cwd: typeof proc?.cwd === 'function' ? proc.cwd() : undefined,
18
+ warn: (message) => console.warn(message),
19
+ };
20
+ }
21
+ export function isProduction(env) {
22
+ return env.NODE_ENV === 'production' || env.KINDGI_ENV === 'production';
23
+ }
24
+ /**
25
+ * The client options with every missing field resolved (see the module
26
+ * comment). Throws when no `apiUrl` or no token can be found.
27
+ */
28
+ export function resolveClientOptions(options, context = defaultContext()) {
29
+ const { env } = context;
30
+ const dev = isProduction(env) ? undefined : findDevRuntime(context.cwd);
31
+ let apiUrl = options.apiUrl ?? nonEmpty(env.KINDGI_API_URL);
32
+ let auth = options.auth ??
33
+ (nonEmpty(env.KINDGI_API_TOKEN) !== undefined
34
+ ? { kind: 'apiToken', token: env.KINDGI_API_TOKEN }
35
+ : undefined);
36
+ if ((apiUrl === undefined || auth === undefined) && dev !== undefined) {
37
+ const used = [];
38
+ if (apiUrl === undefined) {
39
+ apiUrl = dev.apiUrl;
40
+ used.push('KINDGI_API_URL');
41
+ }
42
+ if (auth === undefined) {
43
+ auth = { kind: 'apiToken', token: dev.token };
44
+ used.push('KINDGI_API_TOKEN');
45
+ }
46
+ warnOnce(context, `fallback:${dev.path}`, `Using the running kindgi dev from ${dev.path} for ${used.join(' and ')}. Set them in ${ENV_FILE_HINT}, and in production, where there's no ${DEV_RUNTIME_FILE}.`);
47
+ }
48
+ if (apiUrl === undefined || auth === undefined) {
49
+ const missing = [
50
+ ...(apiUrl === undefined ? ['KINDGI_API_URL'] : []),
51
+ ...(auth === undefined ? ['KINDGI_API_TOKEN'] : []),
52
+ ];
53
+ throw new Error(`createClient(): ${missing.join(' and ')} ${missing.length === 1 ? "isn't" : "aren't"} set. Set ${missing.length === 1 ? 'it' : 'them'} in ${ENV_FILE_HINT}, or pass { apiUrl, auth }.${isProduction(env)
54
+ ? ''
55
+ : ` In development, run \`kindgi dev\` in the app: it writes them to ${DEV_RUNTIME_FILE}, which the client reads.`}`);
56
+ }
57
+ if (dev !== undefined && auth.kind === 'apiToken' && auth.token !== dev.token) {
58
+ if (sameUrl(apiUrl, dev.apiUrl)) {
59
+ warnOnce(context, `stale:${dev.path}:${dev.token}`, `The API token doesn't match the running kindgi dev's (${dev.path}). After \`kindgi dev --reset\` the token changes: copy the new one into ${ENV_FILE_HINT}.`);
60
+ }
61
+ }
62
+ return { ...options, apiUrl, auth };
63
+ }
64
+ /**
65
+ * The running `kindgi dev`'s `apiUrl` and `token`, from the nearest
66
+ * `.kindgirc.json` at or above `from`; `undefined` when there's none, it
67
+ * can't be read, or the runtime can't read files (a browser).
68
+ */
69
+ export function findDevRuntime(from) {
70
+ if (from === undefined)
71
+ return undefined;
72
+ const fs = builtin('node:fs');
73
+ const path = builtin('node:path');
74
+ if (fs === undefined || path === undefined)
75
+ return undefined;
76
+ for (let dir = from;; dir = path.dirname(dir)) {
77
+ const candidate = path.join(dir, DEV_RUNTIME_FILE);
78
+ if (fs.existsSync(candidate))
79
+ return readDevRuntime(fs, candidate);
80
+ if (path.dirname(dir) === dir)
81
+ return undefined;
82
+ }
83
+ }
84
+ function readDevRuntime(fs, file) {
85
+ try {
86
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
87
+ const apiUrl = parsed.apiUrl;
88
+ const token = parsed.token;
89
+ if (typeof apiUrl !== 'string' || apiUrl === '' || typeof token !== 'string' || token === '') {
90
+ return undefined;
91
+ }
92
+ return { apiUrl, token, path: file };
93
+ }
94
+ catch {
95
+ return undefined;
96
+ }
97
+ }
98
+ function builtin(id) {
99
+ const proc = typeof process === 'undefined' ? undefined : process;
100
+ const get = proc
101
+ ?.getBuiltinModule;
102
+ return typeof get === 'function' ? get.call(proc, id) : undefined;
103
+ }
104
+ function nonEmpty(value) {
105
+ return value === undefined || value === '' ? undefined : value;
106
+ }
107
+ function sameUrl(a, b) {
108
+ return a.replace(/\/+$/, '') === b.replace(/\/+$/, '');
109
+ }
110
+ /** Tests only: forget which warnings were printed. */
111
+ export function resetWarningsForTests() {
112
+ warned.clear();
113
+ }
114
+ //# sourceMappingURL=runtime-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-config.js","sourceRoot":"","sources":["../src/runtime-config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindgi/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "@kindgi/sdk — the authoring SDK for Kindgi™. Facade over the individual @kindgi/* packages + @kindgi/client. Unifies pack authoring (defineTool / defineCheck / defineAgent / defineFlow) and client callsites (createClient) behind three sub-paths: /define, /client, /types. Re-export facade; zero behavior.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,27 +18,33 @@
18
18
  "exports": {
19
19
  "./define": {
20
20
  "types": "./dist/define.d.ts",
21
- "import": "./dist/define.js"
21
+ "import": "./dist/define.js",
22
+ "default": "./dist/define.js"
22
23
  },
23
24
  "./client": {
24
25
  "types": "./dist/client.d.ts",
25
- "import": "./dist/client.js"
26
+ "import": "./dist/client.js",
27
+ "default": "./dist/client.js"
26
28
  },
27
29
  "./types": {
28
30
  "types": "./dist/types.d.ts",
29
- "import": "./dist/types.js"
31
+ "import": "./dist/types.js",
32
+ "default": "./dist/types.js"
30
33
  },
31
34
  "./webhooks": {
32
35
  "types": "./dist/webhooks.d.ts",
33
- "import": "./dist/webhooks.js"
36
+ "import": "./dist/webhooks.js",
37
+ "default": "./dist/webhooks.js"
34
38
  },
35
39
  "./build": {
36
40
  "types": "./dist/build.d.ts",
37
- "import": "./dist/build.js"
41
+ "import": "./dist/build.js",
42
+ "default": "./dist/build.js"
38
43
  },
39
44
  ".": {
40
45
  "types": "./dist/index.d.ts",
41
- "import": "./dist/index.js"
46
+ "import": "./dist/index.js",
47
+ "default": "./dist/index.js"
42
48
  },
43
49
  "./package.json": "./package.json"
44
50
  },
@@ -49,15 +55,23 @@
49
55
  "README.md"
50
56
  ],
51
57
  "dependencies": {
52
- "@kindgi/agents": "0.1.0",
53
- "@kindgi/client": "0.1.0",
54
- "@kindgi/crypto": "0.1.0",
55
- "@kindgi/flow": "0.1.0",
56
- "@kindgi/guardrails": "0.1.0",
57
- "@kindgi/handler-runtime": "0.1.0",
58
- "@kindgi/schema": "0.1.0",
59
- "@kindgi/tools": "0.1.0",
60
- "@kindgi/types": "0.1.0"
58
+ "@kindgi/agents": "0.1.2",
59
+ "@kindgi/client": "0.1.2",
60
+ "@kindgi/crypto": "0.1.2",
61
+ "@kindgi/flow": "0.1.2",
62
+ "@kindgi/guardrails": "0.1.2",
63
+ "@kindgi/handler-runtime": "0.1.2",
64
+ "@kindgi/schema": "0.1.2",
65
+ "@kindgi/tools": "0.1.2",
66
+ "@kindgi/types": "0.1.2"
67
+ },
68
+ "peerDependencies": {
69
+ "zod": "^4.0.0"
70
+ },
71
+ "peerDependenciesMeta": {
72
+ "zod": {
73
+ "optional": true
74
+ }
61
75
  },
62
76
  "devDependencies": {
63
77
  "@types/node": "^22.10.5",
@@ -67,7 +81,7 @@
67
81
  "vitest": "^2.1.8"
68
82
  },
69
83
  "engines": {
70
- "node": ">=22.0.0"
84
+ "node": ">=22.12.0"
71
85
  },
72
86
  "publishConfig": {
73
87
  "access": "public",
@@ -12,7 +12,7 @@ description: >
12
12
  kindgi-authoring-guardrails.
13
13
  type: core
14
14
  library: "@kindgi/sdk"
15
- version: "0.4.1"
15
+ version: "0.4.2"
16
16
  sdk_version: "0.0.0"
17
17
  pack_languages: [node]
18
18
  sources:
@@ -237,8 +237,7 @@ concerns, not author-time.
237
237
 
238
238
  - Type surface: `hover any @kindgi/sdk/define export` in your editor
239
239
  for full JSDoc.
240
- - Companion docs: `pnpm --filter @kindgi/sdk exec typedoc` regenerates
241
- markdown API docs at `packages/sdk/docs/`.
240
+ - API reference: https://docs.kindgi.com/v0.1/reference/typescript/sdk/kindgi/sdk/define/ (every `define*` spec, field by field).
242
241
  - Common patterns: the `sample` template's `agents/echo-agent`
243
242
  demonstrates the smallest tool-calling shape.
244
243
 
@@ -13,7 +13,7 @@ description: >
13
13
  kindgi-authoring-agents.
14
14
  type: core
15
15
  library: "@kindgi/sdk"
16
- version: "0.1.1"
16
+ version: "0.1.2"
17
17
  sdk_version: "0.0.0"
18
18
  pack_languages: [node]
19
19
  sources:
@@ -189,9 +189,11 @@ the condition is true. Conditions are JSON:
189
189
  | `and` `or` | `{ op, children: [...] }` |
190
190
  | `not` | `{ op, child }` |
191
191
 
192
- Each operand is `{ literal: … }` or `{ path: … }`. A comparison whose
193
- path doesn't resolve is **false**, `ne` included. So to branch on "not
194
- billing", write `not` around the `eq` (as above), not `ne`.
192
+ Each operand is `{ literal: … }` or `{ path: … }`. When a path doesn't
193
+ resolve, `eq`, `lt`, `lte`, `gt` and `gte` are false and `ne` is true. So
194
+ for the "otherwise" branch, write `not` around the condition (as above),
195
+ rather than a second comparison: it covers exactly what the first edge
196
+ doesn't.
195
197
 
196
198
  **Joining branches.** A node with several incoming edges runs once every
197
199
  one of them is decided and at least one fired. In the example, `reply`
@@ -212,7 +214,7 @@ A node with several incoming edges ignores them.
212
214
  ## Inputs and the output
213
215
 
214
216
  `inputMapping` maps each key to a `{ literal }` or a `{ path }`. Paths are
215
- dot-separated, with no array indexing, rooted at:
217
+ dot-separated (a number segment indexes an array: `items.0.sku`), rooted at:
216
218
  - `runInput.…`: the input the run was started with;
217
219
  - `nodeOutputs.<nodeId>.…`: a step's output. For an agent step, add
218
220
  `.output.<field>` to read its typed answer;
@@ -269,9 +271,10 @@ the output), not on every save.
269
271
  1. **Building a flow without asking what goes in and comes out.** The
270
272
  pack's `echo-flow` proves the runtime works. It isn't a template for
271
273
  the user's flow.
272
- 2. **`ne` on a path that may be missing, to mean "otherwise".** A missing
273
- path makes every comparison false, so neither branch fires and
274
- everything after is skipped. Use `not` around the positive condition.
274
+ 2. **A second comparison for "otherwise".** On a path that may be
275
+ missing, `eq` is false and `ne` is true, and `lt`/`gt` are both false,
276
+ so a hand-written opposite can miss a case or overlap. Use `not` around
277
+ the positive condition: it covers exactly what the first edge doesn't.
275
278
  3. **Reading an agent step's answer at `nodeOutputs.<step>.<field>`.**
276
279
  The typed answer is under `.output`: `nodeOutputs.<step>.output.<field>`.
277
280
  An agent without an `output` schema has only `text`.
@@ -15,7 +15,7 @@ description: >
15
15
  kindgi-authoring-agents.
16
16
  type: core
17
17
  library: "@kindgi/sdk"
18
- version: "0.3.5"
18
+ version: "0.3.6"
19
19
  sdk_version: "0.0.0"
20
20
  pack_languages: [node]
21
21
  sources:
@@ -107,11 +107,17 @@ How the pack tooling reads this file:
107
107
  `configZod`, `configSchema` or `configJsonSchema`, or a top-level
108
108
  `configZod` / `configSchema`), and the declaration's `config` — what
109
109
  the check runs with. It does not record `description`, `budget` or
110
- `judgeCapabilities`.
110
+ `judgeCapabilities`. It checks `config` (none counts as `{}`) against
111
+ the config schema: a config that doesn't fit, or a required field with
112
+ no default left out, is a file error naming where, and `kindgi build`
113
+ refuses the pack.
111
114
  - The **pack service** loads the same module to run the check. It uses
112
115
  the module's `evaluate` export, or the `default` / `check` export when
113
116
  that is a function or has an `evaluate` method — here, the named
114
- `check` export.
117
+ `check` export. A `defineCheck` check's `evaluate` gets the config its
118
+ schema resolves: the schema's defaults applied (a guardrail that
119
+ declares no config gets them all), and a config that doesn't fit
120
+ refused, naming where.
115
121
 
116
122
  ## Validating a declaration in-process
117
123
 
@@ -167,7 +173,10 @@ available to the runtime that evaluates it.
167
173
  - **`config`** — the check's parameters, validated against the check's
168
174
  `configSchema` by `defineGuardrail`. In a pack, the declaration's
169
175
  `config` goes into the index and the check runs with it; without one
170
- it runs with `{}`. `evaluate` receives the config as declared —
176
+ it runs with `{}`. A declaration a pack file default-exports isn't run
177
+ through `defineGuardrail`, so nothing validates its `config`: keep it
178
+ valid against the schema yourself. `evaluate` receives the config as
179
+ declared —
171
180
  schema defaults are not filled in — so handle absent optional fields.
172
181
  - **`action.on-violation`** — `'halt'`, `'retry'` (with
173
182
  `retry.maxAttempts`, 1–10), `'escalate'` (with `escalateTo`),
@@ -177,7 +186,9 @@ available to the runtime that evaluates it.
177
186
  any other action are reported in `AgentTurnResult.violations` and the
178
187
  turn completes. The action handlers in `@kindgi/guardrails`
179
188
  (`retryHandler`, `escalateHandler`, `compensateHandler`, …) record the
180
- intent for callers that act on it.
189
+ intent for callers that act on it. In 0.1 the runtime acts only on
190
+ `halt`: `retry`, `escalate` and `compensate` are recorded on the
191
+ violation, with no second attempt, escalation or compensating call.
181
192
  - **`severity`** — `'info'` / `'warn'` / `'error'` (the default) /
182
193
  `'critical'`. Orthogonal to `action`: logs and dashboards group by
183
194
  severity; execution follows the action. A `log-only` guardrail can
@@ -236,8 +247,9 @@ guardrails: ['acme.no-fabricated-quotes'],
236
247
 
237
248
  At the start of each turn, the runtime resolves these ids against the
238
249
  guardrails available to the run. An id that isn't registered fails the
239
- turn with `unresolved-guardrail`, so register the guardrail before an
240
- agent references it.
250
+ turn before the model is called (`Error [invalid-request]: Agent "…"
251
+ references guardrails not in the registry: <id>`), so register the
252
+ guardrail before an agent references it.
241
253
 
242
254
  ## Changing a guardrail
243
255
 
@@ -277,7 +289,7 @@ ready for the stricter enforcement.
277
289
  - Type surface: hover any `@kindgi/sdk/define` export for full JSDoc;
278
290
  `Guardrail`, `defineGuardrail` and the built-in checks are in
279
291
  `@kindgi/guardrails`.
280
- - Companion docs: `pnpm --filter @kindgi/sdk exec typedoc`.
292
+ - API reference: https://docs.kindgi.com/v0.1/reference/typescript/sdk/kindgi/sdk/define/
281
293
  - Built-in check implementations: `packages/guardrails/src/checks.ts`.
282
294
 
283
295
  ## When the framework itself is the problem
@@ -22,7 +22,7 @@ description: >
22
22
  kindgi-getting-started.
23
23
  type: core
24
24
  library: "@kindgi/sdk"
25
- version: "0.9.1"
25
+ version: "0.9.2"
26
26
  sdk_version: "0.0.0"
27
27
  pack_languages: [node, python]
28
28
  sources:
@@ -492,15 +492,12 @@ into one entry per model (`metadata.models[]`), filters the resulting
492
492
  tenant policy), then sorts survivors in this order:
493
493
 
494
494
  1. **Preferred provider / model.** Tuples matching the agent's
495
- `preferredProvider` (and `preferredModel`, when the `Agent` object
496
- carries one) are promoted to the front.
495
+ `preferredProvider` and `preferredModel` are promoted to the front.
497
496
  - Both set → promote the exact tuple.
498
497
  - Only `preferredModel` set → promote any provider exposing that model.
499
498
  - Only `preferredProvider` set → promote every model of that provider.
500
- `defineAgent` accepts `preferredProvider` but not `preferredModel`
501
- (`DefineAgentSpec` has no such field), so agents built with it can
502
- only express a provider preference here. A Python `Agent` takes
503
- both (`preferred_provider=`, `preferred_model=`).
499
+ `defineAgent` takes both (`preferredProvider`, `preferredModel`), and
500
+ so does a Python `Agent` (`preferred_provider=`, `preferred_model=`).
504
501
  2. **`capability.prefer[]` weights.** If the agent's capability
505
502
  declares `prefer: [{feature: 'thinking', weight: 3}, ...]`, tuples
506
503
  with matching model features (or provider attributes) get higher
@@ -597,7 +594,11 @@ defineAgent({
597
594
  be the FULL npm package name of the adapter — `"@kindgi/adapter-model-anthropic"`,
598
595
  NOT `"anthropic"`. Adapters are registered with the runtime under
599
596
  their full package names, and a short name matches none of them, so
600
- the registration fails. Confirm valid ids with `kindgi adapters list`.
597
+ the registration fails. The model adapters are
598
+ `@kindgi/adapter-model-anthropic`, `@kindgi/adapter-model-gemini`,
599
+ `@kindgi/adapter-model-openai-compat` and
600
+ `@kindgi/adapter-model-in-process`; `kindgi providers presets` shows
601
+ the id each preset uses.
601
602
 
602
603
  1. **`envName` mismatch between the setter (`kindgi secrets set` or `env set`) and `provider.json`.**
603
604
  Both writers use `--env=<name>` (default `local`): `local` is the
@@ -12,7 +12,7 @@ description: >
12
12
  authoring agents is covered by kindgi-authoring-agents.
13
13
  type: core
14
14
  library: "@kindgi/sdk"
15
- version: "0.4.1"
15
+ version: "0.4.4"
16
16
  sdk_version: "0.0.0"
17
17
  pack_languages: [node]
18
18
  sources:
@@ -108,7 +108,7 @@ A handler must return a Promise; one with nothing to `await` can return `Promise
108
108
  The handler gets the **parsed** input, typed `z.infer` of `input` (Zod's output type):
109
109
 
110
110
  - **Defaults.** A `.default()` field is optional to the caller, the model included. The tool's advertised schema doesn't list it as required, and the handler always gets a value.
111
- - **Transforms and refinements.** `.transform()` results and `.refine()` checks apply before the handler runs. A failed refinement comes back as `input-validation-failed`, with the field's path.
111
+ - **Transforms and refinements.** `.transform()` results and `.refine()` checks apply before the handler runs. A failed refinement comes back as `input-validation-failed`.
112
112
  - **Extra keys.** A plain `z.object` accepts them and strips them. Use `z.strictObject` to reject them.
113
113
  - **JSON-Schema-authored tools** get each property's `default` filled in the same way.
114
114
 
@@ -268,13 +268,34 @@ run start via `semver.maxSatisfying`. No implicit `:latest`.
268
268
  changes (breaking schema shape, semantic behavior), not when you
269
269
  save. See the "Iterating on a tool" section above.
270
270
 
271
+ 9. **A package a tool imports, listed only in `devDependencies`.** The
272
+ deployed pack installs the app's production dependencies only, so the
273
+ import works under `kindgi dev` and fails in the image. When a tool
274
+ imports a new package (an ORM client such as `@prisma/client`, an API
275
+ SDK), check that the app's `package.json` lists it under
276
+ `dependencies`. Build-time tools (the `prisma` CLI, `typescript`) stay
277
+ in `devDependencies`. `kindgi dev` warns as soon as a tool imports one
278
+ ("⚠ The pack imports @prisma/client (in kindgi/tools/…), which
279
+ package.json lists only in devDependencies: …"), and `kindgi build`
280
+ refuses the pack until it moves.
281
+ 10. **A tool that needs the app's install scripts in the image.** The
282
+ image installs with scripts off, so the app's `postinstall` /
283
+ `prepare` (`prisma generate`, husky) don't run there; `kindgi build`
284
+ lists them ("✓ The app's own install scripts don't run in the image:
285
+ …"). A tool that uses Prisma's client then fails the build ("@prisma/client
286
+ did not initialize yet"). Add `prisma({ schema: 'prisma/schema.prisma' })`
287
+ (from `@kindgi/sdk/build`; add `config: 'prisma.config.ts'` when the app
288
+ has one) to `image.extensions` in `kindgi.config.ts`. Other generate
289
+ steps: `defineBuildExtension({ name, contextFiles, postInstall: [{ bin, args }] })`.
290
+ Debian packages: `image.systemPackages`. Placeholder env for those steps:
291
+ `image.buildEnv` (never secrets).
292
+
271
293
  ## References
272
294
 
273
295
  - Type surface: `hover any @kindgi/sdk/define export` in your editor
274
296
  for full JSDoc — every field on `DefineToolSpec` / `ToolManifest`
275
297
  documents purpose, when to set it, and gotchas.
276
- - Companion docs: `pnpm --filter @kindgi/sdk exec typedoc` regenerates
277
- markdown API docs at `packages/sdk/docs/`.
298
+ - API reference: https://docs.kindgi.com/v0.1/reference/typescript/sdk/kindgi/sdk/define/ (every `define*` spec, field by field).
278
299
  - Common patterns: check the `sample` template (`kindgi init
279
300
  --template=sample`) for working examples of both authoring modes.
280
301
 
@@ -14,7 +14,7 @@ description: >
14
14
  primitive.
15
15
  type: core
16
16
  library: "@kindgi/sdk"
17
- version: "0.3.2"
17
+ version: "0.3.5"
18
18
  sdk_version: "0.0.0"
19
19
  pack_languages: [node]
20
20
  ---
@@ -70,7 +70,9 @@ pnpm install # or the app's own package manager
70
70
 
71
71
  This adds `kindgi.config.ts` and a `kindgi/` folder beside the app's code,
72
72
  and never creates env files: `kindgi dev` reads the app's own `.env` /
73
- `.env.local`.
73
+ `.env.local`. A package a tool imports must be in the app's `dependencies`,
74
+ not `devDependencies`: the deployed pack installs production dependencies
75
+ only (see `kindgi-authoring-tools`).
74
76
 
75
77
  Either way, `init` adds `@kindgi/sdk` and `@kindgi/cli` to the project's
76
78
  `package.json`, so the project runs the `kindgi` it pins — never a global
@@ -95,22 +97,28 @@ needs on first run), indexes the pack, registers every primitive, and
95
97
  re-registers on every save. The banner prints the API URL, the seeded
96
98
  bearer token, and (if the console is bundled) the `/console/` URL.
97
99
 
98
- The local runtime includes a built-in `demo.echo-agent` you can hit to
99
- verify the harness before authoring anything.
100
+ Until a model provider is registered, agents answer with `dev-echo`, a
101
+ stand-in that calls the agent's first tool with `{"message": <userMessage>}`
102
+ and replies with what the tool returned. It checks the wiring only: it can't
103
+ fill in any other tool input or produce a typed `output` (that turn fails
104
+ with `output-schema-violation`). Register a provider
105
+ (`kindgi-authoring-providers`) before building a real agent.
100
106
 
101
107
  ## First run
102
108
 
103
109
  From a second terminal, with `cd my-pack`:
104
110
 
105
111
  ```bash
106
- pnpm exec kindgi runs start --agent=demo.echo-agent --input='{"userMessage":"hi"}'
112
+ pnpm exec kindgi runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
107
113
  ```
108
114
 
115
+ `my-pack.echo-agent` is the agent the `sample` template ships (`<pack-id>.echo-agent`);
116
+ a `minimal` pack has no agent until you write one.
117
+
109
118
  The CLI reads `.kindgirc.json` (auto-written by `kindgi dev`) for the
110
119
  API URL + token, so second-terminal commands work without flags.
111
120
 
112
- Once you author your own agent, replace `demo.echo-agent` with your
113
- own id.
121
+ Once you author your own agent, run it by its own id.
114
122
 
115
123
  ## Layout
116
124
 
@@ -153,6 +161,48 @@ Most of the setup is automatable, but two require your knowledge:
153
161
  registered — `kindgi providers register --preset=anthropic` with the
154
162
  key in `.env`; see `kindgi-authoring-providers`.
155
163
 
164
+ ## Your app and Kindgi's data
165
+
166
+ When the app keeps something a run did (a ticket a flow triaged, an answer
167
+ an agent gave), its own row stores the run's id, in a column such as
168
+ `kindgi_run_id`. The app starts the run and reads the rest through the API,
169
+ server side, with `createClient()` from `@kindgi/sdk/client`:
170
+
171
+ ```ts
172
+ import { createClient } from '@kindgi/sdk/client';
173
+
174
+ const kindgi = createClient(); // KINDGI_API_URL + KINDGI_API_TOKEN
175
+ const run = await kindgi.runs.start({
176
+ flow: 'my-pack.triage-ticket',
177
+ input: { ticketId },
178
+ options: { wait: false }, // the run id now; run.finished tells you when it ends
179
+ });
180
+ // save run.id as the ticket's kindgi_run_id
181
+ ```
182
+
183
+
184
+ - **Status, output, timing:** `kindgi.runs.get(runId)`
185
+ (`GET /v1/runs/{runId}`); status and timing only: `kindgi.runs.progress(runId)`.
186
+ - **The audit, step by step:** `kindgi.runs.journal(runId)`
187
+ (`GET /v1/runs/{runId}/journal`).
188
+ - **Where an agent's answer came from:** `kindgi.provenance.get(runId)`
189
+ (`GET /v1/provenance/{runId}`). A flow run has none of its own: each agent
190
+ step's `step.completed` entry in the flow's journal names its turn's run
191
+ (`payload.output.runId`).
192
+ - **When a run finished:** the `run.finished` webhook (the run's id and
193
+ outcome, no output; then `runs.get`), not polling.
194
+
195
+ Show it in the app's own UI. **Never:**
196
+
197
+ - **query Kindgi's database**, even on the app's own Postgres server, and
198
+ never map its tables into the app's ORM. Its schema is private and changes
199
+ with every release (migrations only go forward), row-level security guards
200
+ every tenant query, and a runtime Kindgi hosts gives no database access.
201
+ - **link users to Kindgi's console** or any Kindgi UI for this data.
202
+
203
+ To keep a copy (reporting, search), pull it through the API into the app's
204
+ own tables. Docs: https://docs.kindgi.com/v0.1/guides/runs/show-runs-in-your-app/
205
+
156
206
  ## References
157
207
 
158
208
  - Full CLI surface: `kindgi --help`.
@@ -16,7 +16,7 @@ description: >
16
16
  kindgi-python-authoring-agents.
17
17
  type: core
18
18
  library: "kindgi (Python)"
19
- version: "0.1.0"
19
+ version: "0.1.1"
20
20
  sdk_version: "0.0.0"
21
21
  pack_languages: [python]
22
22
  sources:
@@ -200,9 +200,11 @@ the condition is true. Conditions are dicts:
200
200
  | `and` `or` | `{"op", "children": [...]}` |
201
201
  | `not` | `{"op", "child"}` |
202
202
 
203
- Each operand is `{"literal": …}` or `{"path": …}`. A comparison whose
204
- path doesn't resolve is **false**, `ne` included. So to branch on "not
205
- billing", write `not` around the `eq` (as above), not `ne`. A condition
203
+ Each operand is `{"literal": …}` or `{"path": …}`. When a path doesn't
204
+ resolve, `eq`, `lt`, `lte`, `gt` and `gte` are false and `ne` is true. So
205
+ for the "otherwise" branch, write `not` around the condition (as above),
206
+ rather than a second comparison: it covers exactly what the first edge
207
+ doesn't. A condition
206
208
  used twice is easiest as a module-level constant (`IS_BILLING`).
207
209
 
208
210
  **Joining branches.** A node with several incoming edges runs once every
@@ -227,7 +229,7 @@ A node with several incoming edges ignores them.
227
229
  Its keys are the tool's input **as it travels**: a pydantic field's name,
228
230
  or its alias if it has one (a `customer_id` field is the key
229
231
  `customer_id`; with `alias="customerId"`, it's `customerId`). Paths are
230
- dot-separated, with no array indexing, rooted at:
232
+ dot-separated (a number segment indexes an array: `items.0.sku`), rooted at:
231
233
  - `runInput.…`: the input the run was started with;
232
234
  - `nodeOutputs.<nodeId>.…`: a step's output. For an agent step, add
233
235
  `.output.<field>` to read its typed answer;
@@ -289,9 +291,10 @@ the output), not on every save.
289
291
  1. **Building a flow without asking what goes in and comes out.** The
290
292
  pack's `echo_flow` proves the runtime works. It isn't a template for
291
293
  the user's flow.
292
- 2. **`ne` on a path that may be missing, to mean "otherwise".** A missing
293
- path makes every comparison false, so neither branch fires and
294
- everything after is skipped. Use `not` around the positive condition.
294
+ 2. **A second comparison for "otherwise".** On a path that may be
295
+ missing, `eq` is false and `ne` is true, and `lt`/`gt` are both false,
296
+ so a hand-written opposite can miss a case or overlap. Use `not` around
297
+ the positive condition: it covers exactly what the first edge doesn't.
295
298
  3. **Reading an agent step's answer at `nodeOutputs.<step>.<field>`.**
296
299
  The typed answer is under `.output`: `nodeOutputs.<step>.output.<field>`.
297
300
  An agent without `output=` has only `text`.
@@ -14,7 +14,7 @@ description: >
14
14
  kindgi-python-authoring-tools.
15
15
  type: core
16
16
  library: "kindgi (Python)"
17
- version: "0.1.0"
17
+ version: "0.1.1"
18
18
  sdk_version: "0.0.0"
19
19
  pack_languages: [python]
20
20
  sources:
@@ -106,7 +106,9 @@ def no_fabricated_quotes(config: Config, trace: RunTrace) -> CheckResult:
106
106
  In an agent turn a failed `halt` guardrail fails the turn
107
107
  (`guardrail-violation`) and the answer is not stored; any other action
108
108
  reports the failure in the turn result's `violations` and the turn
109
- completes.
109
+ completes. In 0.1 the runtime acts only on `halt`: `retry`, `escalate`
110
+ and `compensate` are recorded on the violation, with no second attempt,
111
+ escalation or compensating call.
110
112
  - **`severity`** — `"info"`, `"warn"`, `"error"` (default), `"critical"`.
111
113
  Independent of the action: dashboards group by severity, execution
112
114
  follows the action.
@@ -147,8 +149,9 @@ brief_writer = Agent(..., guardrails=[no_fabricated_quotes])
147
149
  ```
148
150
 
149
151
  The `Guardrail` object (or its id). `kindgi dev` registers the pack's
150
- guardrails; an agent naming an id with no registered guardrail fails
151
- its turn (`unresolved-guardrail`).
152
+ guardrails; an agent naming an id with no registered guardrail fails the
153
+ turn before the model is called (`Error [invalid-request]: Agent "…"
154
+ references guardrails not in the registry: <id>`).
152
155
 
153
156
  ## Common mistakes
154
157
 
@@ -158,8 +161,8 @@ its turn (`unresolved-guardrail`).
158
161
  2. **Snake_case keys in `config=`.** It is keyed like the wire — the
159
162
  model's aliases (`{"minLookups": 2}`), not the field names.
160
163
  3. **Both `on_violation=` and `action=`, or neither** — `DefinitionError`.
161
- 4. **Expecting retries from `halt`.** `halt` stops the turn; use
162
- `action={"on-violation": "retry", …}` for another attempt.
164
+ 4. **Expecting another attempt.** `halt` stops the turn, and in 0.1
165
+ `retry` doesn't run the turn again: it's only recorded.
163
166
  5. **Calling a model from the check.** Not available; keep checks pure.
164
167
  6. **Raising for a broken rule.** Return `CheckResult(passed=False,
165
168
  reason=…)`; an exception is an evaluation error, not a violation.
@@ -14,7 +14,7 @@ description: >
14
14
  kindgi-python-getting-started.
15
15
  type: core
16
16
  library: "kindgi (Python)"
17
- version: "0.1.0"
17
+ version: "0.1.1"
18
18
  sdk_version: "0.0.0"
19
19
  pack_languages: [python]
20
20
  sources:
@@ -290,6 +290,12 @@ removed field, a narrower type — not on every save.
290
290
  and an agent's tool approval gate asks before it on first use.
291
291
  10. **`mutating=False` on a tool that writes.** A dry run then runs it
292
292
  for real.
293
+ 11. **A package a tool imports, only in a dev group.** The deployed pack
294
+ installs without dev dependencies (`uv sync --no-dev`, or Poetry's
295
+ main group only), so the import works under `kindgi dev` and fails in
296
+ the image. Put what tools import in `[project].dependencies` (in a
297
+ Poetry 1 app, `[tool.poetry.dependencies]`); test and build tools stay
298
+ in dev groups.
293
299
 
294
300
  ## When the framework itself is the problem
295
301
 
@@ -14,7 +14,7 @@ description: >
14
14
  kindgi-python-authoring-agents; models by kindgi-authoring-providers.
15
15
  type: core
16
16
  library: "kindgi (Python)"
17
- version: "0.1.0"
17
+ version: "0.1.3"
18
18
  sdk_version: "0.0.0"
19
19
  pack_languages: [python]
20
20
  sources:
@@ -26,7 +26,7 @@ sources:
26
26
  # Getting started with Kindgi in Python
27
27
 
28
28
  > **Running `kindgi`:** a Python pack has no Node project, so the
29
- > `kindgi` CLI (a Node 22+ program) is the one on `PATH`. Python
29
+ > `kindgi` CLI (a Node 22.12+ program) is the one on `PATH`. Python
30
30
  > commands run in the pack's environment: `uv run …`.
31
31
 
32
32
  ## What a pack is
@@ -90,7 +90,9 @@ name (`from acme.text import normalize`); inside `kindgi/`, import the pack's
90
90
  modules relatively. Don't add an `__init__.py` to `kindgi/` — the folder
91
91
  would then shadow the `kindgi` package. `kindgi dev` reads the app's `.env`
92
92
  / `.env.local` — keys already there reach the tools as environment
93
- variables.
93
+ variables. A package a tool imports must be in the app's main dependencies,
94
+ not a dev group: the deployed pack installs without dev dependencies (see
95
+ `kindgi-python-authoring-tools`).
94
96
 
95
97
  ## Layout of the template
96
98
 
@@ -180,6 +182,37 @@ for event in client.runs.stream(str(run.id)):
180
182
  `AsyncKindgi` is the asyncio twin. `kindgi dev` prints the URL and the
181
183
  token; `.kindgirc.json` in the pack holds them for the CLI.
182
184
 
185
+ ## Your app and Kindgi's data
186
+
187
+ When the app keeps something a run did (a ticket a flow triaged, an answer
188
+ an agent gave), its own row stores the run's id, in a column such as
189
+ `kindgi_run_id` (`run = client.runs.start(flow=…, input=…, options={"wait": False})`,
190
+ then `run.id`). The app reads the rest through the API, server side, with
191
+ `Kindgi()` from `kindgi.client`:
192
+
193
+ - **Status, output, timing:** `client.runs.get(run_id)`
194
+ (`GET /v1/runs/{runId}`); status and timing only: `client.runs.progress(run_id)`.
195
+ - **The audit, step by step:** `client.runs.journal(run_id).data`
196
+ (`GET /v1/runs/{runId}/journal`).
197
+ - **Where an agent's answer came from:** `client.provenance.get(run_id)`
198
+ (`GET /v1/provenance/{runId}`). A flow run has none of its own: each agent
199
+ step's `step.completed` entry in the flow's journal names its turn's run
200
+ (`entry.payload["output"]["runId"]`).
201
+ - **When a run finished:** the `run.finished` webhook (the run's id and
202
+ outcome, no output; then `runs.get`), not polling.
203
+
204
+ Show it in the app's own UI. **Never:**
205
+
206
+ - **query Kindgi's database**, even on the app's own Postgres server, and
207
+ never map its tables into the app's ORM (SQLAlchemy, Django models). Its
208
+ schema is private and changes with every release (migrations only go
209
+ forward), row-level security guards every tenant query, and a runtime
210
+ Kindgi hosts gives no database access.
211
+ - **link users to Kindgi's console** or any Kindgi UI for this data.
212
+
213
+ To keep a copy (reporting, search), pull it through the API into the app's
214
+ own tables. Docs: https://docs.kindgi.com/v0.1/guides/runs/show-runs-in-your-app/
215
+
183
216
  ## Build an image
184
217
 
185
218
  `kindgi build --env=<name>` (an `[tool.kindgi.environments.<name>]`
package/src/client.ts CHANGED
@@ -21,9 +21,39 @@
21
21
  */
22
22
 
23
23
  // ---- Client factory + top-level shape ----
24
- export { createClient } from '@kindgi/client';
24
+ import { createClient as createKindgiClient } from '@kindgi/client';
25
+ import type { ClientOptions, KindgiClient } from '@kindgi/client';
26
+
27
+ import { resolveClientOptions } from './runtime-config.js';
28
+
25
29
  export type { KindgiClient } from '@kindgi/client';
26
30
 
31
+ /**
32
+ * A client for the app's Kindgi runtime. Every option is optional:
33
+ *
34
+ * - `apiUrl` and `auth` come from `KINDGI_API_URL` and `KINDGI_API_TOKEN`;
35
+ * - outside production, when those aren't set, from the running
36
+ * `kindgi dev` (the nearest `.kindgirc.json`), with a one-time warning to
37
+ * put them in the app's env file;
38
+ * - a token that doesn't match the running `kindgi dev`'s for the same
39
+ * `apiUrl` (after `kindgi dev --reset`) is warned about once.
40
+ *
41
+ * Production (`NODE_ENV` or `KINDGI_ENV` = `production`) never reads
42
+ * `.kindgirc.json`: there, the env or explicit options must say. In a
43
+ * browser, pass `apiUrl` and `auth`. `@kindgi/client`'s `createClient` is
44
+ * the explicit form underneath.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { createClient } from '@kindgi/sdk/client';
49
+ *
50
+ * const kindgi = createClient();
51
+ * ```
52
+ */
53
+ export function createClient(options: Partial<ClientOptions> = {}): KindgiClient {
54
+ return createKindgiClient(resolveClientOptions(options));
55
+ }
56
+
27
57
  // ---- Resource client types + per-resource input shapes ----
28
58
  export type {
29
59
  AdapterConfigureInput,
@@ -0,0 +1,180 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ /**
5
+ * Where an app's client finds its Kindgi runtime when `createClient` is
6
+ * called without `apiUrl` / `auth`:
7
+ *
8
+ * 1. `KINDGI_API_URL` and `KINDGI_API_TOKEN` from the environment;
9
+ * 2. outside production, the running `kindgi dev`, from the nearest
10
+ * `.kindgirc.json` at or above the working directory (it records the
11
+ * runtime's `apiUrl` and the dev `token`), with a one-time warning to
12
+ * put them in the app's env file;
13
+ * 3. otherwise, an error that says what to set.
14
+ *
15
+ * Production (`NODE_ENV` or `KINDGI_ENV` set to `production`) never reads
16
+ * `.kindgirc.json`. Whatever the source, a token that differs from the
17
+ * running `kindgi dev`'s for the same `apiUrl` (the stale token after
18
+ * `kindgi dev --reset`) is warned about once.
19
+ *
20
+ * Node only: the file is read through `process.getBuiltinModule`, never a
21
+ * static `node:` import, so the module stays safe to bundle for browsers,
22
+ * where only explicit options apply.
23
+ */
24
+
25
+ import type { AuthConfig, ClientOptions } from '@kindgi/client';
26
+
27
+ /** The file `kindgi dev` writes in the pack directory. */
28
+ export const DEV_RUNTIME_FILE = '.kindgirc.json';
29
+
30
+ /** What a client needs from a running `kindgi dev`. */
31
+ export interface DevRuntime {
32
+ readonly apiUrl: string;
33
+ readonly token: string;
34
+ /** The `.kindgirc.json` it came from. */
35
+ readonly path: string;
36
+ }
37
+
38
+ /** Where the lookup runs: the process's own environment by default (tests pass their own). */
39
+ export interface RuntimeConfigContext {
40
+ readonly env: Readonly<Record<string, string | undefined>>;
41
+ /** The directory the `.kindgirc.json` search starts from. */
42
+ readonly cwd: string | undefined;
43
+ readonly warn: (message: string) => void;
44
+ }
45
+
46
+ const ENV_FILE_HINT = 'your env file (.env / .env.local)';
47
+
48
+ const warned = new Set<string>();
49
+
50
+ function warnOnce(context: RuntimeConfigContext, key: string, message: string): void {
51
+ if (warned.has(key)) return;
52
+ warned.add(key);
53
+ context.warn(`[kindgi] ${message}`);
54
+ }
55
+
56
+ export function defaultContext(): RuntimeConfigContext {
57
+ const proc = typeof process === 'undefined' ? undefined : process;
58
+ return {
59
+ env: proc?.env ?? {},
60
+ cwd: typeof proc?.cwd === 'function' ? proc.cwd() : undefined,
61
+ warn: (message) => console.warn(message),
62
+ };
63
+ }
64
+
65
+ export function isProduction(env: RuntimeConfigContext['env']): boolean {
66
+ return env.NODE_ENV === 'production' || env.KINDGI_ENV === 'production';
67
+ }
68
+
69
+ /**
70
+ * The client options with every missing field resolved (see the module
71
+ * comment). Throws when no `apiUrl` or no token can be found.
72
+ */
73
+ export function resolveClientOptions(
74
+ options: Partial<ClientOptions>,
75
+ context: RuntimeConfigContext = defaultContext(),
76
+ ): ClientOptions {
77
+ const { env } = context;
78
+ const dev = isProduction(env) ? undefined : findDevRuntime(context.cwd);
79
+
80
+ let apiUrl = options.apiUrl ?? nonEmpty(env.KINDGI_API_URL);
81
+ let auth: AuthConfig | undefined =
82
+ options.auth ??
83
+ (nonEmpty(env.KINDGI_API_TOKEN) !== undefined
84
+ ? { kind: 'apiToken', token: env.KINDGI_API_TOKEN as string }
85
+ : undefined);
86
+
87
+ if ((apiUrl === undefined || auth === undefined) && dev !== undefined) {
88
+ const used: string[] = [];
89
+ if (apiUrl === undefined) {
90
+ apiUrl = dev.apiUrl;
91
+ used.push('KINDGI_API_URL');
92
+ }
93
+ if (auth === undefined) {
94
+ auth = { kind: 'apiToken', token: dev.token };
95
+ used.push('KINDGI_API_TOKEN');
96
+ }
97
+ warnOnce(
98
+ context,
99
+ `fallback:${dev.path}`,
100
+ `Using the running kindgi dev from ${dev.path} for ${used.join(' and ')}. Set them in ${ENV_FILE_HINT}, and in production, where there's no ${DEV_RUNTIME_FILE}.`,
101
+ );
102
+ }
103
+
104
+ if (apiUrl === undefined || auth === undefined) {
105
+ const missing = [
106
+ ...(apiUrl === undefined ? ['KINDGI_API_URL'] : []),
107
+ ...(auth === undefined ? ['KINDGI_API_TOKEN'] : []),
108
+ ];
109
+ throw new Error(
110
+ `createClient(): ${missing.join(' and ')} ${missing.length === 1 ? "isn't" : "aren't"} set. Set ${missing.length === 1 ? 'it' : 'them'} in ${ENV_FILE_HINT}, or pass { apiUrl, auth }.${
111
+ isProduction(env)
112
+ ? ''
113
+ : ` In development, run \`kindgi dev\` in the app: it writes them to ${DEV_RUNTIME_FILE}, which the client reads.`
114
+ }`,
115
+ );
116
+ }
117
+
118
+ if (dev !== undefined && auth.kind === 'apiToken' && auth.token !== dev.token) {
119
+ if (sameUrl(apiUrl, dev.apiUrl)) {
120
+ warnOnce(
121
+ context,
122
+ `stale:${dev.path}:${dev.token}`,
123
+ `The API token doesn't match the running kindgi dev's (${dev.path}). After \`kindgi dev --reset\` the token changes: copy the new one into ${ENV_FILE_HINT}.`,
124
+ );
125
+ }
126
+ }
127
+
128
+ return { ...options, apiUrl, auth };
129
+ }
130
+
131
+ /**
132
+ * The running `kindgi dev`'s `apiUrl` and `token`, from the nearest
133
+ * `.kindgirc.json` at or above `from`; `undefined` when there's none, it
134
+ * can't be read, or the runtime can't read files (a browser).
135
+ */
136
+ export function findDevRuntime(from: string | undefined): DevRuntime | undefined {
137
+ if (from === undefined) return undefined;
138
+ const fs = builtin<typeof import('node:fs')>('node:fs');
139
+ const path = builtin<typeof import('node:path')>('node:path');
140
+ if (fs === undefined || path === undefined) return undefined;
141
+ for (let dir = from; ; dir = path.dirname(dir)) {
142
+ const candidate = path.join(dir, DEV_RUNTIME_FILE);
143
+ if (fs.existsSync(candidate)) return readDevRuntime(fs, candidate);
144
+ if (path.dirname(dir) === dir) return undefined;
145
+ }
146
+ }
147
+
148
+ function readDevRuntime(fs: typeof import('node:fs'), file: string): DevRuntime | undefined {
149
+ try {
150
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf8')) as Record<string, unknown>;
151
+ const apiUrl = parsed.apiUrl;
152
+ const token = parsed.token;
153
+ if (typeof apiUrl !== 'string' || apiUrl === '' || typeof token !== 'string' || token === '') {
154
+ return undefined;
155
+ }
156
+ return { apiUrl, token, path: file };
157
+ } catch {
158
+ return undefined;
159
+ }
160
+ }
161
+
162
+ function builtin<T>(id: string): T | undefined {
163
+ const proc = typeof process === 'undefined' ? undefined : process;
164
+ const get = (proc as { getBuiltinModule?: (id: string) => unknown } | undefined)
165
+ ?.getBuiltinModule;
166
+ return typeof get === 'function' ? (get.call(proc, id) as T | undefined) : undefined;
167
+ }
168
+
169
+ function nonEmpty(value: string | undefined): string | undefined {
170
+ return value === undefined || value === '' ? undefined : value;
171
+ }
172
+
173
+ function sameUrl(a: string, b: string): boolean {
174
+ return a.replace(/\/+$/, '') === b.replace(/\/+$/, '');
175
+ }
176
+
177
+ /** Tests only: forget which warnings were printed. */
178
+ export function resetWarningsForTests(): void {
179
+ warned.clear();
180
+ }