@kindgi/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -6
- package/dist/client.d.ts +19 -14
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +27 -1
- package/dist/client.js.map +1 -1
- package/dist/runtime-config.d.ts +53 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +114 -0
- package/dist/runtime-config.js.map +1 -0
- package/package.json +18 -10
- package/skills/kindgi-authoring-agents/SKILL.md +2 -3
- package/skills/kindgi-authoring-flows/SKILL.md +11 -8
- package/skills/kindgi-authoring-guardrails/SKILL.md +12 -6
- package/skills/kindgi-authoring-providers/SKILL.md +9 -8
- package/skills/kindgi-authoring-tools/SKILL.md +14 -4
- package/skills/kindgi-getting-started/SKILL.md +15 -7
- package/skills/kindgi-python-authoring-flows/SKILL.md +11 -8
- package/skills/kindgi-python-authoring-guardrails/SKILL.md +9 -6
- package/skills/kindgi-python-authoring-tools/SKILL.md +7 -1
- package/skills/kindgi-python-getting-started/SKILL.md +4 -2
- package/src/client.ts +31 -1
- package/src/runtime-config.ts +180 -0
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
|
-
|
|
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
|
|
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
|
-
*
|
|
4
|
+
* A client for the app's Kindgi runtime. Every option is optional:
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
15
|
-
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createClient } from '@kindgi/sdk/client';
|
|
16
21
|
*
|
|
17
|
-
*
|
|
22
|
+
* const kindgi = createClient();
|
|
23
|
+
* ```
|
|
18
24
|
*/
|
|
19
|
-
export
|
|
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';
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
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) ----
|
package/dist/client.js.map
CHANGED
|
@@ -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;
|
|
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.
|
|
3
|
+
"version": "0.1.1",
|
|
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": {
|
|
@@ -49,15 +49,23 @@
|
|
|
49
49
|
"README.md"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@kindgi/agents": "0.1.
|
|
53
|
-
"@kindgi/client": "0.1.
|
|
54
|
-
"@kindgi/crypto": "0.1.
|
|
55
|
-
"@kindgi/flow": "0.1.
|
|
56
|
-
"@kindgi/guardrails": "0.1.
|
|
57
|
-
"@kindgi/handler-runtime": "0.1.
|
|
58
|
-
"@kindgi/schema": "0.1.
|
|
59
|
-
"@kindgi/tools": "0.1.
|
|
60
|
-
"@kindgi/types": "0.1.
|
|
52
|
+
"@kindgi/agents": "0.1.1",
|
|
53
|
+
"@kindgi/client": "0.1.1",
|
|
54
|
+
"@kindgi/crypto": "0.1.1",
|
|
55
|
+
"@kindgi/flow": "0.1.1",
|
|
56
|
+
"@kindgi/guardrails": "0.1.1",
|
|
57
|
+
"@kindgi/handler-runtime": "0.1.1",
|
|
58
|
+
"@kindgi/schema": "0.1.1",
|
|
59
|
+
"@kindgi/tools": "0.1.1",
|
|
60
|
+
"@kindgi/types": "0.1.1"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"zod": "^4.0.0"
|
|
64
|
+
},
|
|
65
|
+
"peerDependenciesMeta": {
|
|
66
|
+
"zod": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
61
69
|
},
|
|
62
70
|
"devDependencies": {
|
|
63
71
|
"@types/node": "^22.10.5",
|
|
@@ -12,7 +12,7 @@ description: >
|
|
|
12
12
|
kindgi-authoring-guardrails.
|
|
13
13
|
type: core
|
|
14
14
|
library: "@kindgi/sdk"
|
|
15
|
-
version: "0.4.
|
|
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
|
-
-
|
|
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.
|
|
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: … }`.
|
|
193
|
-
|
|
194
|
-
|
|
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
|
|
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.
|
|
273
|
-
|
|
274
|
-
|
|
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.
|
|
18
|
+
version: "0.3.6"
|
|
19
19
|
sdk_version: "0.0.0"
|
|
20
20
|
pack_languages: [node]
|
|
21
21
|
sources:
|
|
@@ -167,7 +167,10 @@ available to the runtime that evaluates it.
|
|
|
167
167
|
- **`config`** — the check's parameters, validated against the check's
|
|
168
168
|
`configSchema` by `defineGuardrail`. In a pack, the declaration's
|
|
169
169
|
`config` goes into the index and the check runs with it; without one
|
|
170
|
-
it runs with `{}`.
|
|
170
|
+
it runs with `{}`. A declaration a pack file default-exports isn't run
|
|
171
|
+
through `defineGuardrail`, so nothing validates its `config`: keep it
|
|
172
|
+
valid against the schema yourself. `evaluate` receives the config as
|
|
173
|
+
declared —
|
|
171
174
|
schema defaults are not filled in — so handle absent optional fields.
|
|
172
175
|
- **`action.on-violation`** — `'halt'`, `'retry'` (with
|
|
173
176
|
`retry.maxAttempts`, 1–10), `'escalate'` (with `escalateTo`),
|
|
@@ -177,7 +180,9 @@ available to the runtime that evaluates it.
|
|
|
177
180
|
any other action are reported in `AgentTurnResult.violations` and the
|
|
178
181
|
turn completes. The action handlers in `@kindgi/guardrails`
|
|
179
182
|
(`retryHandler`, `escalateHandler`, `compensateHandler`, …) record the
|
|
180
|
-
intent for callers that act on it.
|
|
183
|
+
intent for callers that act on it. In 0.1 the runtime acts only on
|
|
184
|
+
`halt`: `retry`, `escalate` and `compensate` are recorded on the
|
|
185
|
+
violation, with no second attempt, escalation or compensating call.
|
|
181
186
|
- **`severity`** — `'info'` / `'warn'` / `'error'` (the default) /
|
|
182
187
|
`'critical'`. Orthogonal to `action`: logs and dashboards group by
|
|
183
188
|
severity; execution follows the action. A `log-only` guardrail can
|
|
@@ -236,8 +241,9 @@ guardrails: ['acme.no-fabricated-quotes'],
|
|
|
236
241
|
|
|
237
242
|
At the start of each turn, the runtime resolves these ids against the
|
|
238
243
|
guardrails available to the run. An id that isn't registered fails the
|
|
239
|
-
turn
|
|
240
|
-
|
|
244
|
+
turn before the model is called (`Error [invalid-request]: Agent "…"
|
|
245
|
+
references guardrails not in the registry: <id>`), so register the
|
|
246
|
+
guardrail before an agent references it.
|
|
241
247
|
|
|
242
248
|
## Changing a guardrail
|
|
243
249
|
|
|
@@ -277,7 +283,7 @@ ready for the stricter enforcement.
|
|
|
277
283
|
- Type surface: hover any `@kindgi/sdk/define` export for full JSDoc;
|
|
278
284
|
`Guardrail`, `defineGuardrail` and the built-in checks are in
|
|
279
285
|
`@kindgi/guardrails`.
|
|
280
|
-
-
|
|
286
|
+
- API reference: https://docs.kindgi.com/v0.1/reference/typescript/sdk/kindgi/sdk/define/
|
|
281
287
|
- Built-in check implementations: `packages/guardrails/src/checks.ts`.
|
|
282
288
|
|
|
283
289
|
## 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.
|
|
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`
|
|
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`
|
|
501
|
-
|
|
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.
|
|
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.
|
|
15
|
+
version: "0.4.3"
|
|
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
|
|
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,23 @@ 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
|
+
|
|
271
282
|
## References
|
|
272
283
|
|
|
273
284
|
- Type surface: `hover any @kindgi/sdk/define export` in your editor
|
|
274
285
|
for full JSDoc — every field on `DefineToolSpec` / `ToolManifest`
|
|
275
286
|
documents purpose, when to set it, and gotchas.
|
|
276
|
-
-
|
|
277
|
-
markdown API docs at `packages/sdk/docs/`.
|
|
287
|
+
- API reference: https://docs.kindgi.com/v0.1/reference/typescript/sdk/kindgi/sdk/define/ (every `define*` spec, field by field).
|
|
278
288
|
- Common patterns: check the `sample` template (`kindgi init
|
|
279
289
|
--template=sample`) for working examples of both authoring modes.
|
|
280
290
|
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
primitive.
|
|
15
15
|
type: core
|
|
16
16
|
library: "@kindgi/sdk"
|
|
17
|
-
version: "0.3.
|
|
17
|
+
version: "0.3.4"
|
|
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
|
-
|
|
99
|
-
|
|
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=
|
|
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,
|
|
113
|
-
own id.
|
|
121
|
+
Once you author your own agent, run it by its own id.
|
|
114
122
|
|
|
115
123
|
## Layout
|
|
116
124
|
|
|
@@ -16,7 +16,7 @@ description: >
|
|
|
16
16
|
kindgi-python-authoring-agents.
|
|
17
17
|
type: core
|
|
18
18
|
library: "kindgi (Python)"
|
|
19
|
-
version: "0.1.
|
|
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": …}`.
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
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.
|
|
293
|
-
|
|
294
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
162
|
-
`
|
|
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.
|
|
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.
|
|
17
|
+
version: "0.1.1"
|
|
18
18
|
sdk_version: "0.0.0"
|
|
19
19
|
pack_languages: [python]
|
|
20
20
|
sources:
|
|
@@ -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
|
|
package/src/client.ts
CHANGED
|
@@ -21,9 +21,39 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
// ---- Client factory + top-level shape ----
|
|
24
|
-
|
|
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
|
+
}
|