@dench.com/cli 3.7.0-staging.387 → 3.8.0-staging.389
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 +43 -44
- package/agent-config.ts +5 -55
- package/app-sdk.d.ts +121 -16
- package/chat-spawn.ts +11 -2
- package/chat.ts +1 -1
- package/context-settings.ts +86 -0
- package/cron.ts +19 -0
- package/dench.ts +202 -83
- package/environments.ts +694 -0
- package/lib/api-schemas.ts +4 -0
- package/lib/app-sdk-catalog.generated.ts +31 -17
- package/lib/command-args.ts +19 -4
- package/lib/command-help.ts +17 -2
- package/lib/command-registry.ts +222 -8
- package/lib/environment-archive.ts +255 -0
- package/lib/environment-cli-args.ts +96 -0
- package/lib/environment-contract.ts +237 -0
- package/lib/environment-execution.ts +769 -0
- package/lib/environment-source.ts +165 -0
- package/lib/load-dev-env.mjs +3 -1
- package/lib/load-dev-env.test.mjs +26 -9
- package/lib/parent-argv.ts +20 -0
- package/lib/references.generated.json +20 -12
- package/package.json +5 -3
- package/workspace-secrets.ts +231 -0
- package/agent.ts +0 -37
package/README.md
CHANGED
|
@@ -112,9 +112,8 @@ next commands. Add `--json` for structured output.
|
|
|
112
112
|
|
|
113
113
|
Browse and search the user's past chat threads in this workspace (own +
|
|
114
114
|
shared). Useful for pulling in earlier context when the user refers to
|
|
115
|
-
something they discussed before. The chat
|
|
116
|
-
|
|
117
|
-
`read_past_chat`); this CLI surface is the same data accessed via bash.
|
|
115
|
+
something they discussed before. The shared chat harness uses these same CLI
|
|
116
|
+
commands through its execution tool; there is no separate agent-only chat-history command family.
|
|
118
117
|
|
|
119
118
|
```bash
|
|
120
119
|
dench chat list [--query <substr>] [--limit N] [--include-archived] [--json]
|
|
@@ -391,54 +390,53 @@ Outreach needs a workspace member's credentials (a `dench signin` session, a
|
|
|
391
390
|
member-bound `DENCH_API_KEY`, or an agent session); an organization-only key
|
|
392
391
|
is refused.
|
|
393
392
|
|
|
394
|
-
## Workspace
|
|
393
|
+
## Workspace files and reusable setup
|
|
395
394
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
395
|
+
Use `dench files` for saved workspace documents and `dench stage` for outputs.
|
|
396
|
+
Harness A keeps a separate scratch directory per job. The old filesystem and
|
|
397
|
+
home-sync daemons are legacy compatibility infrastructure; they are not needed
|
|
398
|
+
to execute a command or restore a saved environment.
|
|
400
399
|
|
|
401
|
-
```
|
|
402
|
-
dench
|
|
403
|
-
dench
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
| `daemon.healthy` | The process is alive and cached progress passes freshness checks, with a short startup grace before the first scan. |
|
|
409
|
-
| `daemon.workspaceMatches` | The cached daemon serves the requested workspace root. A mismatch also makes `healthy` false. |
|
|
410
|
-
| `daemon.state.mountWorker` | Cached worker state, PID, last progress time, restart count, and error. |
|
|
411
|
-
| `counts.onDisk` | Last successful scan's count, only for the matching root; `null` when unavailable. Not a fresh count. |
|
|
412
|
-
| `counts.inConvex`, `counts.onlyOnDisk`, `counts.onlyInConvex`, `counts.hashMismatches` | `null`: these counts are unknown, not zero. |
|
|
413
|
-
| `driftStatus`, `drift` | `"not_checked"` and `[]`; an empty list does not prove that files match. |
|
|
414
|
-
|
|
415
|
-
Status exits **1** when the daemon is missing, dead, stale, stalled, or
|
|
416
|
-
serves a different root, including with `--json`; exit **0** means cached
|
|
417
|
-
health passed, not that every pending write has synced. The legacy
|
|
418
|
-
`--no-hash` and `--drift-limit N` flags remain accepted but have no effect.
|
|
419
|
-
|
|
420
|
-
To pull a workspace file through the running daemon:
|
|
421
|
-
|
|
422
|
-
```bash
|
|
423
|
-
dench fs pull "/reports/example.csv" --wait --timeout-ms 10000 --json
|
|
400
|
+
```sh
|
|
401
|
+
dench files cat /reports/example.md
|
|
402
|
+
dench env list --json
|
|
403
|
+
dench env save openai-report --path ./openai-report --json
|
|
404
|
+
dench env show openai-report --json
|
|
405
|
+
dench env run openai-report -- node report.mjs
|
|
406
|
+
dench secrets set OPENAI_API_KEY --stdin < /private/path/key.txt
|
|
424
407
|
```
|
|
425
408
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
409
|
+
A saved directory requires `dench-env.json`, `SKILL.md`, an explicit file allowlist
|
|
410
|
+
and verification commands. It builds dependencies once, publishes a ready
|
|
411
|
+
revision, and reuses the prepared snapshot or compatible bundle. Only the chosen
|
|
412
|
+
environment is loaded. Secrets are encrypted separately and injected into the
|
|
413
|
+
executing process; they never become dependency archives or memory facts.
|
|
414
|
+
|
|
415
|
+
New automation runs select the latest ready revision; active jobs and their
|
|
416
|
+
children retain the resolved revision. State is isolated by job or automation
|
|
417
|
+
unless an explicit shared state key is chosen. State writes and refreshed
|
|
418
|
+
credential files use compare-and-set versions; conflicts do not replay business
|
|
419
|
+
operations. Rotate credentials without reinstalling packages.
|
|
420
|
+
|
|
421
|
+
On a different local OS/runtime, explicitly prepare a separate platform cache
|
|
422
|
+
once using `dench env run openai-report --prepare-local -- node report.mjs`.
|
|
423
|
+
Normal ready runs never invoke a package installer. The local executor provides
|
|
424
|
+
separate directories, not operating-system isolation.
|
|
425
|
+
|
|
426
|
+
See [the environment reference](https://dench.com/references/ENVIRONMENTS.md)
|
|
427
|
+
for the manifest, cold-start costs, mutable state and recovery receipts. Use
|
|
428
|
+
`dench memory search/save` for facts and preferences, `dench context set` for
|
|
429
|
+
standing context, and `dench secrets` for credentials. An old memory claiming
|
|
430
|
+
that a service is configured cannot replace a successful storage receipt.
|
|
434
431
|
|
|
435
432
|
## Sandbox / API key auth (no `dench signin` required)
|
|
436
433
|
|
|
437
434
|
Inside a Dench sandbox the Dench API key is set in the environment when the
|
|
438
435
|
sandbox starts. When `DENCH_API_KEY` is present, the CLI skips the
|
|
439
436
|
`dench signin` flow entirely and uses the key as a bearer for every org-scoped
|
|
440
|
-
command — `dench crm`, `dench files`,
|
|
441
|
-
|
|
437
|
+
command — `dench crm`, `dench files`, and other workspace operations. The
|
|
438
|
+
selected connection determines the backend; hosted credentials stay scoped to
|
|
439
|
+
the authorized workspace and job.
|
|
442
440
|
|
|
443
441
|
```bash
|
|
444
442
|
export DENCH_API_KEY=dench_pk_live_…
|
|
@@ -477,7 +475,8 @@ dench context --sections organisation,user,memory --json
|
|
|
477
475
|
```
|
|
478
476
|
|
|
479
477
|
Section names are `identity`, `organisation`, `user`, `memory`, `rules`, and
|
|
480
|
-
`tools`. Multiple names use one query.
|
|
481
|
-
|
|
482
|
-
|
|
478
|
+
`tools`. Multiple names use one query. Reading never fills profiles or writes
|
|
479
|
+
memory. Edit the selected context with
|
|
480
|
+
`dench context set <identity|organisation|user|tools|rules> --stdin`; older profile
|
|
481
|
+
aliases remain compatibility paths. Personal USER context is unavailable to shared
|
|
483
482
|
channel sessions. `dench context --json` keeps its existing workspace overview.
|
package/agent-config.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `dench identity / organisation / user / bootstrap / model /
|
|
2
|
+
* `dench identity / organisation / user / bootstrap / model /
|
|
3
3
|
* mem aggregate` — CLI surface for the OpenClaw-
|
|
4
4
|
* parity self-updating harness.
|
|
5
5
|
*
|
|
@@ -15,14 +15,13 @@
|
|
|
15
15
|
* identity show | edit
|
|
16
16
|
* organisation show | edit
|
|
17
17
|
* user show | edit
|
|
18
|
-
* tools show |
|
|
18
|
+
* tools show | edit
|
|
19
19
|
* memory show | append <text> | set (MEMORY.md aggregate)
|
|
20
20
|
* bootstrap status | complete | reopen | template
|
|
21
21
|
* model default <id> | model default clear |
|
|
22
22
|
* model thread <threadId> <id> | model thread <threadId> clear
|
|
23
|
-
* daily today | daily show <YYYY-MM-DD> | daily append <text>
|
|
24
23
|
*
|
|
25
|
-
* `edit` / `
|
|
24
|
+
* `edit` / `template` open the
|
|
26
25
|
* user's `$EDITOR` (default `vi`) to capture the new body. The
|
|
27
26
|
* stdin path is also supported via `--stdin`, useful in scripts.
|
|
28
27
|
*/
|
|
@@ -496,66 +495,17 @@ export async function runModelCommand(ctx: CliCtx): Promise<void> {
|
|
|
496
495
|
await handleModel(ctx, ctx.args);
|
|
497
496
|
}
|
|
498
497
|
|
|
499
|
-
/**
|
|
500
|
-
* Daily activity log subcommands (real fileTree files under
|
|
501
|
-
* `/workspace/daily/`). Today / show <YYYY-MM-DD> read the inline
|
|
502
|
-
* cache; append delegates to the agent action so timestamps and
|
|
503
|
-
* heading wrappers stay consistent.
|
|
504
|
-
*/
|
|
505
|
-
export async function runDailyCommand(ctx: CliCtx): Promise<void> {
|
|
506
|
-
consumeOutputFlag(ctx);
|
|
507
|
-
const sub = ctx.args[0];
|
|
508
|
-
if (!sub || sub === "today") {
|
|
509
|
-
const today = new Date();
|
|
510
|
-
const yyyy = today.getUTCFullYear();
|
|
511
|
-
const mm = String(today.getUTCMonth() + 1).padStart(2, "0");
|
|
512
|
-
const dd = String(today.getUTCDate()).padStart(2, "0");
|
|
513
|
-
const path = `/daily/${yyyy}-${mm}-${dd}.md`;
|
|
514
|
-
// Daily logs are real files — fetch via the workspace file API
|
|
515
|
-
// hop so the same auth (Convex Auth or API key) works.
|
|
516
|
-
out(
|
|
517
|
-
ctx,
|
|
518
|
-
ctx.jsonOutput
|
|
519
|
-
? { path }
|
|
520
|
-
: `Today's daily log lives at ${path}. Use \`dench fs cat ${path}\` to read it, or open it in the workspace UI.`,
|
|
521
|
-
);
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
if (sub === "show") {
|
|
525
|
-
const date = ctx.args[1];
|
|
526
|
-
if (!date) {
|
|
527
|
-
throw new AgentConfigCliError("Usage: dench daily show <YYYY-MM-DD>");
|
|
528
|
-
}
|
|
529
|
-
out(
|
|
530
|
-
ctx,
|
|
531
|
-
ctx.jsonOutput
|
|
532
|
-
? { path: `/daily/${date}.md` }
|
|
533
|
-
: `Daily log for ${date} lives at /daily/${date}.md. Use \`dench fs cat\` to read it.`,
|
|
534
|
-
);
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
537
|
-
if (sub === "append") {
|
|
538
|
-
throw new AgentConfigCliError(
|
|
539
|
-
"Daily append is currently only available via the agent's `append_daily_log` tool. Use the chat to log entries with timestamps.",
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
throw new AgentConfigCliError(
|
|
543
|
-
`Unknown daily subcommand: "${sub}". Try "today", "show <YYYY-MM-DD>".`,
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
498
|
export function printAgentConfigCliHelp(): void {
|
|
548
499
|
console.log(
|
|
549
|
-
`
|
|
500
|
+
`Workspace context compatibility aliases (prefer dench context --sections / context set):
|
|
550
501
|
|
|
551
502
|
dench identity show | edit IDENTITY.md (org-wide)
|
|
552
503
|
dench organisation show | edit ORGANISATION.md
|
|
553
504
|
dench user show | edit USER.md (per-member)
|
|
554
|
-
dench tools show |
|
|
505
|
+
dench tools show | edit TOOLS.md notes section
|
|
555
506
|
dench mem show | append <text> | set MEMORY.md aggregate
|
|
556
507
|
dench bootstrap status | complete | reopen | template
|
|
557
508
|
dench model default <id|clear> | model thread <threadId> <id|clear>
|
|
558
|
-
dench daily today | show <YYYY-MM-DD> Activity log paths
|
|
559
509
|
|
|
560
510
|
Editing flags:
|
|
561
511
|
--content "<markdown>" Inline body
|
package/app-sdk.d.ts
CHANGED
|
@@ -12,6 +12,67 @@
|
|
|
12
12
|
// ── Operation request/response types (from cli/lib/api-schemas.ts) ──────
|
|
13
13
|
|
|
14
14
|
export interface ApiRequests {
|
|
15
|
+
/** List workspace-shared reusable setups and ready revisions (metadata only). — `dench env list` — GET /api/v1/environments */
|
|
16
|
+
"environments.list": {
|
|
17
|
+
limit?: number;
|
|
18
|
+
cursor?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Inspect a saved setup, its manifest, and an optional immutable revision. — `dench env show` — GET /api/v1/environments/{name} */
|
|
21
|
+
"environments.get": {
|
|
22
|
+
name: string;
|
|
23
|
+
revision?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Reserve an owner-bound source upload. Prefix the uploaded bytes with the returned proof before saving. — `dench env save` — POST /api/v1/environments/uploads */
|
|
26
|
+
"environments.upload": {
|
|
27
|
+
kind: "source";
|
|
28
|
+
size: number;
|
|
29
|
+
sha256: string;
|
|
30
|
+
};
|
|
31
|
+
/** Publish an explicitly saved source and manifest for one-time preparation. Returns pending/building until verified; does not run the task. — `dench env save` — POST /api/v1/environments/{name} */
|
|
32
|
+
"environments.save": {
|
|
33
|
+
name: string;
|
|
34
|
+
manifest: {
|
|
35
|
+
version: 1;
|
|
36
|
+
name: string;
|
|
37
|
+
purpose: string;
|
|
38
|
+
files: string[];
|
|
39
|
+
prepare: string[];
|
|
40
|
+
verify: string[];
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
secretEnv?: Record<string, string>;
|
|
43
|
+
secretFiles?: {
|
|
44
|
+
path: string;
|
|
45
|
+
secret: string;
|
|
46
|
+
mutable?: boolean;
|
|
47
|
+
}[];
|
|
48
|
+
statePaths?: string[];
|
|
49
|
+
portable?: boolean;
|
|
50
|
+
};
|
|
51
|
+
uploadId: string;
|
|
52
|
+
storageId: string;
|
|
53
|
+
sha256: string;
|
|
54
|
+
};
|
|
55
|
+
/** Retire a saved setup from new selection; retain immutable revisions and active job evidence. — `dench env delete` — DELETE /api/v1/environments/{name} */
|
|
56
|
+
"environments.delete": {
|
|
57
|
+
name: string;
|
|
58
|
+
};
|
|
59
|
+
/** List workspace-shared credential names, kinds and versions; never values. — `dench secrets list` — GET /api/v1/secrets */
|
|
60
|
+
"secrets.list": {
|
|
61
|
+
limit?: number;
|
|
62
|
+
cursor?: string;
|
|
63
|
+
};
|
|
64
|
+
/** Encrypt a workspace credential. Use CLI stdin or the secure chat form; return metadata only. Do not put values in chat, manifests or scripts. — `dench secrets set` — PUT /api/v1/secrets/{name} */
|
|
65
|
+
"secrets.set": {
|
|
66
|
+
name: string;
|
|
67
|
+
kind: "env" | "file";
|
|
68
|
+
value: string;
|
|
69
|
+
expectedVersion?: number;
|
|
70
|
+
};
|
|
71
|
+
/** Revoke a workspace credential for future resolution, optionally checking its version. — `dench secrets delete` — DELETE /api/v1/secrets/{name} */
|
|
72
|
+
"secrets.delete": {
|
|
73
|
+
name: string;
|
|
74
|
+
expectedVersion?: number;
|
|
75
|
+
};
|
|
15
76
|
/** Create an idempotent empty campaign draft with the caller's available sender pools. Does not launch or send. — `dench campaign draft` — POST /api/v1/campaigns/drafts */
|
|
16
77
|
"campaigns.drafts.create": {
|
|
17
78
|
/** Your stable id for this request, 8-64 characters. Retrying with the same id returns the same draft instead of creating another. */
|
|
@@ -699,6 +760,7 @@ export interface ApiRequests {
|
|
|
699
760
|
};
|
|
700
761
|
/** Start a new chat-turn workflow. — `dench chat new` — POST /api/v1/chat/threads */
|
|
701
762
|
"chat.new": {
|
|
763
|
+
environment?: string;
|
|
702
764
|
/** The user message that starts the turn. */
|
|
703
765
|
prompt: string;
|
|
704
766
|
model?: string;
|
|
@@ -3235,30 +3297,34 @@ export interface ApiRequests {
|
|
|
3235
3297
|
"browser.session.status": Record<string, unknown>;
|
|
3236
3298
|
/** Stop a Dench Browser session and release the remote browser. — `dench browser session stop` — DELETE /api/v1/browser/sessions/{sessionId} */
|
|
3237
3299
|
"browser.session.stop": Record<string, unknown>;
|
|
3238
|
-
/** Read IDENTITY.md. — `dench identity
|
|
3300
|
+
/** Read IDENTITY.md. — `dench context --sections identity` — GET /api/v1/agent-config/identity */
|
|
3239
3301
|
"agentConfig.identity.show": Record<string, unknown>;
|
|
3240
|
-
/** Update IDENTITY.md. — `dench identity
|
|
3302
|
+
/** Update IDENTITY.md. — `dench context set identity` — PUT /api/v1/agent-config/identity */
|
|
3241
3303
|
"agentConfig.identity.edit": {
|
|
3242
3304
|
content: string;
|
|
3243
3305
|
};
|
|
3244
|
-
/** Read ORGANISATION.md. — `dench organisation
|
|
3306
|
+
/** Read ORGANISATION.md. — `dench context --sections organisation` — GET /api/v1/agent-config/organisation */
|
|
3245
3307
|
"agentConfig.organisation.show": Record<string, unknown>;
|
|
3246
|
-
/** Update ORGANISATION.md. — `dench organisation
|
|
3308
|
+
/** Update ORGANISATION.md. — `dench context set organisation` — PUT /api/v1/agent-config/organisation */
|
|
3247
3309
|
"agentConfig.organisation.edit": {
|
|
3248
3310
|
content: string;
|
|
3249
3311
|
};
|
|
3250
|
-
/** Read USER.md. — `dench user
|
|
3312
|
+
/** Read USER.md. — `dench context --sections user` — GET /api/v1/agent-config/user */
|
|
3251
3313
|
"agentConfig.user.show": Record<string, unknown>;
|
|
3252
|
-
/** Update USER.md. — `dench user
|
|
3314
|
+
/** Update USER.md. — `dench context set user` — PUT /api/v1/agent-config/user */
|
|
3253
3315
|
"agentConfig.user.edit": {
|
|
3254
3316
|
content: string;
|
|
3255
3317
|
};
|
|
3256
|
-
/** Read tool notes. — `dench tools
|
|
3318
|
+
/** Read tool notes. — `dench context --sections tools` — GET /api/v1/agent-config/tools */
|
|
3257
3319
|
"agentConfig.tools.show": Record<string, unknown>;
|
|
3258
|
-
/** Update tool notes. — `dench tools
|
|
3320
|
+
/** Update tool notes. — `dench context set tools` — PUT /api/v1/agent-config/tools */
|
|
3259
3321
|
"agentConfig.tools.edit": {
|
|
3260
3322
|
content: string;
|
|
3261
3323
|
};
|
|
3324
|
+
/** Update standing workspace rules; credentials belong in secrets and runnable setup in environments. — `dench context set rules` — PUT /api/v1/context/rules */
|
|
3325
|
+
"workspace.context.rules.set": {
|
|
3326
|
+
content: string;
|
|
3327
|
+
};
|
|
3262
3328
|
/** Read aggregate memory. — `dench mem show` — GET /api/v1/agent-config/mem */
|
|
3263
3329
|
"agentConfig.mem.show": Record<string, unknown>;
|
|
3264
3330
|
/** Update aggregate memory. — `dench mem set` — PUT /api/v1/agent-config/mem */
|
|
@@ -3347,6 +3413,20 @@ export interface ApiRequests {
|
|
|
3347
3413
|
}
|
|
3348
3414
|
|
|
3349
3415
|
export interface ApiResponses {
|
|
3416
|
+
"environments.list": unknown;
|
|
3417
|
+
"environments.get": unknown;
|
|
3418
|
+
"environments.upload": unknown;
|
|
3419
|
+
"environments.save": unknown;
|
|
3420
|
+
"environments.delete": unknown;
|
|
3421
|
+
"secrets.list": unknown;
|
|
3422
|
+
"secrets.set": {
|
|
3423
|
+
name: string;
|
|
3424
|
+
kind: "env" | "file";
|
|
3425
|
+
version: number;
|
|
3426
|
+
revision: number;
|
|
3427
|
+
status: "stored";
|
|
3428
|
+
};
|
|
3429
|
+
"secrets.delete": unknown;
|
|
3350
3430
|
"campaigns.drafts.create": {
|
|
3351
3431
|
sequenceId: string;
|
|
3352
3432
|
};
|
|
@@ -7255,6 +7335,7 @@ export interface ApiResponses {
|
|
|
7255
7335
|
template?: string;
|
|
7256
7336
|
};
|
|
7257
7337
|
};
|
|
7338
|
+
"workspace.context.rules.set": unknown;
|
|
7258
7339
|
"agentConfig.mem.show": {
|
|
7259
7340
|
agentConfig: {
|
|
7260
7341
|
identity: {
|
|
@@ -7566,9 +7647,9 @@ export type DenchApiTree = {
|
|
|
7566
7647
|
today(args?: Req<"agentConfig.daily.today">): Promise<Res<"agentConfig.daily.today">>;
|
|
7567
7648
|
};
|
|
7568
7649
|
identity: {
|
|
7569
|
-
/** Update IDENTITY.md. (`dench identity
|
|
7650
|
+
/** Update IDENTITY.md. (`dench context set identity`) */
|
|
7570
7651
|
edit(args: Req<"agentConfig.identity.edit">): Promise<Res<"agentConfig.identity.edit">>;
|
|
7571
|
-
/** Read IDENTITY.md. (`dench identity
|
|
7652
|
+
/** Read IDENTITY.md. (`dench context --sections identity`) */
|
|
7572
7653
|
show(args?: Req<"agentConfig.identity.show">): Promise<Res<"agentConfig.identity.show">>;
|
|
7573
7654
|
};
|
|
7574
7655
|
mem: {
|
|
@@ -7584,21 +7665,21 @@ export type DenchApiTree = {
|
|
|
7584
7665
|
thread(args: Req<"agentConfig.model.thread">): Promise<Res<"agentConfig.model.thread">>;
|
|
7585
7666
|
};
|
|
7586
7667
|
organisation: {
|
|
7587
|
-
/** Update ORGANISATION.md. (`dench organisation
|
|
7668
|
+
/** Update ORGANISATION.md. (`dench context set organisation`) */
|
|
7588
7669
|
edit(args: Req<"agentConfig.organisation.edit">): Promise<Res<"agentConfig.organisation.edit">>;
|
|
7589
|
-
/** Read ORGANISATION.md. (`dench organisation
|
|
7670
|
+
/** Read ORGANISATION.md. (`dench context --sections organisation`) */
|
|
7590
7671
|
show(args?: Req<"agentConfig.organisation.show">): Promise<Res<"agentConfig.organisation.show">>;
|
|
7591
7672
|
};
|
|
7592
7673
|
tools: {
|
|
7593
|
-
/** Update tool notes. (`dench tools
|
|
7674
|
+
/** Update tool notes. (`dench context set tools`) */
|
|
7594
7675
|
edit(args: Req<"agentConfig.tools.edit">): Promise<Res<"agentConfig.tools.edit">>;
|
|
7595
|
-
/** Read tool notes. (`dench tools
|
|
7676
|
+
/** Read tool notes. (`dench context --sections tools`) */
|
|
7596
7677
|
show(args?: Req<"agentConfig.tools.show">): Promise<Res<"agentConfig.tools.show">>;
|
|
7597
7678
|
};
|
|
7598
7679
|
user: {
|
|
7599
|
-
/** Update USER.md. (`dench user
|
|
7680
|
+
/** Update USER.md. (`dench context set user`) */
|
|
7600
7681
|
edit(args: Req<"agentConfig.user.edit">): Promise<Res<"agentConfig.user.edit">>;
|
|
7601
|
-
/** Read USER.md. (`dench user
|
|
7682
|
+
/** Read USER.md. (`dench context --sections user`) */
|
|
7602
7683
|
show(args?: Req<"agentConfig.user.show">): Promise<Res<"agentConfig.user.show">>;
|
|
7603
7684
|
};
|
|
7604
7685
|
};
|
|
@@ -8362,6 +8443,18 @@ export type DenchApiTree = {
|
|
|
8362
8443
|
preview(args: Req<"enrichment.search.preview">): Promise<Res<"enrichment.search.preview">>;
|
|
8363
8444
|
};
|
|
8364
8445
|
};
|
|
8446
|
+
environments: {
|
|
8447
|
+
/** Retire a saved setup from new selection; retain immutable revisions and active job evidence. (`dench env delete`) */
|
|
8448
|
+
delete(args: Req<"environments.delete">): Promise<Res<"environments.delete">>;
|
|
8449
|
+
/** Inspect a saved setup, its manifest, and an optional immutable revision. (`dench env show`) */
|
|
8450
|
+
get(args: Req<"environments.get">): Promise<Res<"environments.get">>;
|
|
8451
|
+
/** List workspace-shared reusable setups and ready revisions (metadata only). (`dench env list`) */
|
|
8452
|
+
list(args?: Req<"environments.list">): Promise<Res<"environments.list">>;
|
|
8453
|
+
/** Publish an explicitly saved source and manifest for one-time preparation. Returns pending/building until verified; does not run the task. (`dench env save`) */
|
|
8454
|
+
save(args: Req<"environments.save">): Promise<Res<"environments.save">>;
|
|
8455
|
+
/** Reserve an owner-bound source upload. Prefix the uploaded bytes with the returned proof before saving. (`dench env save`) */
|
|
8456
|
+
upload(args: Req<"environments.upload">): Promise<Res<"environments.upload">>;
|
|
8457
|
+
};
|
|
8365
8458
|
files: {
|
|
8366
8459
|
/** Read a workspace file's text. (`dench files cat`) */
|
|
8367
8460
|
content(args?: Req<"files.content">): Promise<Res<"files.content">>;
|
|
@@ -8506,6 +8599,14 @@ export type DenchApiTree = {
|
|
|
8506
8599
|
/** Search the web through the Dench gateway. (`dench search`) */
|
|
8507
8600
|
web(args: Req<"search.web">): Promise<Res<"search.web">>;
|
|
8508
8601
|
};
|
|
8602
|
+
secrets: {
|
|
8603
|
+
/** Revoke a workspace credential for future resolution, optionally checking its version. (`dench secrets delete`) */
|
|
8604
|
+
delete(args: Req<"secrets.delete">): Promise<Res<"secrets.delete">>;
|
|
8605
|
+
/** List workspace-shared credential names, kinds and versions; never values. (`dench secrets list`) */
|
|
8606
|
+
list(args?: Req<"secrets.list">): Promise<Res<"secrets.list">>;
|
|
8607
|
+
/** Encrypt a workspace credential. Use CLI stdin or the secure chat form; return metadata only. Do not put values in chat, manifests or scripts. (`dench secrets set`) */
|
|
8608
|
+
set(args: Req<"secrets.set">): Promise<Res<"secrets.set">>;
|
|
8609
|
+
};
|
|
8509
8610
|
settings: {
|
|
8510
8611
|
sync: {
|
|
8511
8612
|
/** Read the workspace's Sync switches (accounts from email, contacts from LinkedIn, Slack Connect, calendar, dossier, tasks from email, lead auto-qualify, summaries and Brief). All on by default. (`dench settings sync`) */
|
|
@@ -8575,6 +8676,10 @@ export type DenchApiTree = {
|
|
|
8575
8676
|
/** Return the current Dench workspace context for the bearer. (`dench context`) */
|
|
8576
8677
|
(args?: Req<"workspace.context">): Promise<Res<"workspace.context">>;
|
|
8577
8678
|
$call(args?: Req<"workspace.context">): Promise<Res<"workspace.context">>;
|
|
8679
|
+
rules: {
|
|
8680
|
+
/** Update standing workspace rules; credentials belong in secrets and runnable setup in environments. (`dench context set rules`) */
|
|
8681
|
+
set(args: Req<"workspace.context.rules.set">): Promise<Res<"workspace.context.rules.set">>;
|
|
8682
|
+
};
|
|
8578
8683
|
/** Read selected scoped workspace documents and a compact context index without writes. (`dench context --sections`) */
|
|
8579
8684
|
sections(args?: Req<"workspace.context.sections">): Promise<Res<"workspace.context.sections">>;
|
|
8580
8685
|
};
|
package/chat-spawn.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* (`/api/chat/[runId]/stream`) and pretty-prints text deltas + tool
|
|
17
17
|
* calls until the workflow finishes.
|
|
18
18
|
*/
|
|
19
|
-
import { hasFlag } from "./lib/cli-args";
|
|
19
|
+
import { assertNoUnknownFlags, hasFlag } from "./lib/cli-args";
|
|
20
20
|
|
|
21
21
|
class ChatSpawnError extends Error {}
|
|
22
22
|
|
|
@@ -47,6 +47,10 @@ function consumeFlagValue(args: string[], name: string): string | undefined {
|
|
|
47
47
|
const idx = args.indexOf(name);
|
|
48
48
|
if (idx === -1) return undefined;
|
|
49
49
|
const value = args[idx + 1];
|
|
50
|
+
if (!value || value.startsWith("--"))
|
|
51
|
+
throw new ChatSpawnError(`${name} requires a value.`);
|
|
52
|
+
if (args.indexOf(name, idx + 1) !== -1)
|
|
53
|
+
throw new ChatSpawnError(`Duplicate flag: ${name}`);
|
|
50
54
|
args.splice(idx, 2);
|
|
51
55
|
return value;
|
|
52
56
|
}
|
|
@@ -372,17 +376,21 @@ function joinPositionalsExceptFlags(args: string[]): string {
|
|
|
372
376
|
}
|
|
373
377
|
|
|
374
378
|
async function runChatNew(ctx: ChatSpawnContext): Promise<void> {
|
|
379
|
+
const environment = consumeFlagValue(ctx.args, "--environment");
|
|
380
|
+
if (environment !== undefined && !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
381
|
+
throw new ChatSpawnError("Invalid environment name");
|
|
375
382
|
const model = consumeFlagValue(ctx.args, "--model");
|
|
376
383
|
const fileContextPath = consumeFlagValue(ctx.args, "--file-context");
|
|
377
384
|
const visibility = consumeFlagValue(ctx.args, "--visibility");
|
|
378
385
|
const title = consumeFlagValue(ctx.args, "--title");
|
|
379
386
|
const yolo = consumeFlag(ctx.args, "--yolo");
|
|
380
387
|
const follow = consumeFlag(ctx.args, "--follow");
|
|
388
|
+
assertNoUnknownFlags(ctx.args, "chat new");
|
|
381
389
|
|
|
382
390
|
const prompt = joinPositionalsExceptFlags(ctx.args);
|
|
383
391
|
if (!prompt) {
|
|
384
392
|
throw new ChatSpawnError(
|
|
385
|
-
'Usage: dench chat new "<prompt>" [--model M] [--file-context PATH] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]',
|
|
393
|
+
'Usage: dench chat new "<prompt>" [--environment NAME] [--model M] [--file-context PATH] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]',
|
|
386
394
|
);
|
|
387
395
|
}
|
|
388
396
|
|
|
@@ -391,6 +399,7 @@ async function runChatNew(ctx: ChatSpawnContext): Promise<void> {
|
|
|
391
399
|
ctx.runtime.bearerToken,
|
|
392
400
|
{
|
|
393
401
|
prompt,
|
|
402
|
+
...(environment ? { environment } : {}),
|
|
394
403
|
model,
|
|
395
404
|
fileContextPath,
|
|
396
405
|
visibility,
|
package/chat.ts
CHANGED
|
@@ -145,7 +145,7 @@ PERMANENTLY delete one or more past chat threads (and all their messages):
|
|
|
145
145
|
server REFUSES to delete them unless \`--include-automated\` is set.
|
|
146
146
|
|
|
147
147
|
Spawn a new chat-turn workflow (same agent loop as the web UI):
|
|
148
|
-
dench chat new "<prompt>" [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
148
|
+
dench chat new "<prompt>" [--environment NAME] [--model <m>] [--file-context <path>] [--visibility private|shared] [--title T] [--yolo] [--follow] [--json]
|
|
149
149
|
dench chat send <thread-id> "<message>" [--model <m>] [--yolo] [--follow] [--json]
|
|
150
150
|
dench chat follow <thread-id> [--json]
|
|
151
151
|
dench chat tree [<root-thread-id>] [--limit N] [--json]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { makeFunctionReference } from "convex/server";
|
|
3
|
+
import { CliArgError } from "./lib/cli-args";
|
|
4
|
+
import { parseEnvironmentOptions } from "./lib/environment-cli-args";
|
|
5
|
+
|
|
6
|
+
export const CONTEXT_SECTIONS = [
|
|
7
|
+
"identity",
|
|
8
|
+
"organisation",
|
|
9
|
+
"user",
|
|
10
|
+
"tools",
|
|
11
|
+
"rules",
|
|
12
|
+
] as const;
|
|
13
|
+
export function validateContextSet(input: readonly string[]) {
|
|
14
|
+
const parsed = parseEnvironmentOptions(
|
|
15
|
+
input,
|
|
16
|
+
["--content"],
|
|
17
|
+
["--stdin", "--json"],
|
|
18
|
+
);
|
|
19
|
+
const [verb, section] = parsed.positional;
|
|
20
|
+
if (
|
|
21
|
+
verb !== "set" ||
|
|
22
|
+
parsed.positional.length !== 2 ||
|
|
23
|
+
!CONTEXT_SECTIONS.includes(section as never) ||
|
|
24
|
+
parsed.child.length
|
|
25
|
+
)
|
|
26
|
+
throw new CliArgError(
|
|
27
|
+
`Usage: dench context set <${CONTEXT_SECTIONS.join("|")}> --stdin | --content <text> [--json]`,
|
|
28
|
+
);
|
|
29
|
+
if (
|
|
30
|
+
Number(parsed.flags["--stdin"] === true) +
|
|
31
|
+
Number(typeof parsed.flags["--content"] === "string") !==
|
|
32
|
+
1
|
|
33
|
+
)
|
|
34
|
+
throw new CliArgError(
|
|
35
|
+
"Choose exactly one of --stdin or --content. Store credentials with dench secrets set, never in context.",
|
|
36
|
+
);
|
|
37
|
+
return {
|
|
38
|
+
section: section as (typeof CONTEXT_SECTIONS)[number],
|
|
39
|
+
content: parsed.flags["--content"] as string | undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function runContextSet(opts: {
|
|
44
|
+
convex: ConvexHttpClient;
|
|
45
|
+
args: string[];
|
|
46
|
+
sessionToken?: string;
|
|
47
|
+
readStdin?: () => Promise<string>;
|
|
48
|
+
}) {
|
|
49
|
+
const parsed = validateContextSet(opts.args);
|
|
50
|
+
const content =
|
|
51
|
+
parsed.content ??
|
|
52
|
+
(await (
|
|
53
|
+
opts.readStdin ??
|
|
54
|
+
(async () => {
|
|
55
|
+
const chunks: Buffer[] = [];
|
|
56
|
+
let size = 0;
|
|
57
|
+
for await (const chunk of process.stdin) {
|
|
58
|
+
size += Buffer.byteLength(chunk);
|
|
59
|
+
if (size > 32000)
|
|
60
|
+
throw new CliArgError(
|
|
61
|
+
"Context exceeds 32 KB. Keep standing instructions short; use workspace files for references.",
|
|
62
|
+
);
|
|
63
|
+
chunks.push(Buffer.from(chunk));
|
|
64
|
+
}
|
|
65
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
66
|
+
})
|
|
67
|
+
)());
|
|
68
|
+
if (!content.trim() || Buffer.byteLength(content) > 32000)
|
|
69
|
+
throw new CliArgError("Context must contain 1–32000 UTF-8 bytes.");
|
|
70
|
+
const fn = {
|
|
71
|
+
identity: "updateIdentity",
|
|
72
|
+
organisation: "updateOrganisation",
|
|
73
|
+
user: "updateUserProfile",
|
|
74
|
+
tools: "updateToolsNotes",
|
|
75
|
+
rules: "writeMagicFileFromUI",
|
|
76
|
+
}[parsed.section];
|
|
77
|
+
await opts.convex.mutation(
|
|
78
|
+
makeFunctionReference<"mutation">(`functions/agentConfig:${fn}`),
|
|
79
|
+
{
|
|
80
|
+
...(opts.sessionToken ? { sessionToken: opts.sessionToken } : {}),
|
|
81
|
+
content,
|
|
82
|
+
...(parsed.section === "rules" ? { path: "/RULES.md" } : {}),
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
console.log(JSON.stringify({ ok: true, section: parsed.section }));
|
|
86
|
+
}
|
package/cron.ts
CHANGED
|
@@ -392,6 +392,9 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
392
392
|
"--prompt is required (becomes the user message of each cron-fired chat thread)",
|
|
393
393
|
);
|
|
394
394
|
}
|
|
395
|
+
const environment = getFlag(ctx.args, "--environment");
|
|
396
|
+
if (environment && !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
397
|
+
throw new CronCliError("Invalid environment name");
|
|
395
398
|
const description = getFlag(ctx.args, "--description");
|
|
396
399
|
const overlapPolicy = getFlag(ctx.args, "--overlap") as
|
|
397
400
|
| "skip"
|
|
@@ -431,6 +434,7 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
431
434
|
triggerType,
|
|
432
435
|
name,
|
|
433
436
|
prompt,
|
|
437
|
+
environment,
|
|
434
438
|
description,
|
|
435
439
|
overlapPolicy,
|
|
436
440
|
disabled,
|
|
@@ -462,6 +466,7 @@ async function runCreate(ctx: CronCliContext): Promise<void> {
|
|
|
462
466
|
const created = (await callMutation(ctx, createCronTriggerRef, {
|
|
463
467
|
name,
|
|
464
468
|
prompt,
|
|
469
|
+
environment,
|
|
465
470
|
triggerType,
|
|
466
471
|
schedule,
|
|
467
472
|
description,
|
|
@@ -551,6 +556,7 @@ async function runSecret(ctx: CronCliContext): Promise<void> {
|
|
|
551
556
|
}
|
|
552
557
|
|
|
553
558
|
export type AppOrCrmCreateBase = {
|
|
559
|
+
environment?: string;
|
|
554
560
|
triggerType: "composio" | "crm";
|
|
555
561
|
name: string;
|
|
556
562
|
prompt: string;
|
|
@@ -584,6 +590,7 @@ export function buildAppOrCrmCreateBody(
|
|
|
584
590
|
const body: Record<string, unknown> = {
|
|
585
591
|
name: base.name,
|
|
586
592
|
prompt: base.prompt,
|
|
593
|
+
environment: base.environment,
|
|
587
594
|
triggerType: base.triggerType,
|
|
588
595
|
description: base.description,
|
|
589
596
|
overlapPolicy: base.overlapPolicy,
|
|
@@ -731,6 +738,15 @@ async function runEvents(ctx: CronCliContext): Promise<void> {
|
|
|
731
738
|
|
|
732
739
|
async function runUpdate(ctx: CronCliContext): Promise<void> {
|
|
733
740
|
const jobId = shift(ctx.args, "job id");
|
|
741
|
+
const environment = getFlag(ctx.args, "--environment");
|
|
742
|
+
const clearEnvironment = hasFlag(ctx.args, "--clear-environment");
|
|
743
|
+
if (
|
|
744
|
+
environment &&
|
|
745
|
+
(clearEnvironment || !/^[a-z][a-z0-9-]{0,63}$/.test(environment))
|
|
746
|
+
)
|
|
747
|
+
throw new CronCliError(
|
|
748
|
+
"Choose a valid --environment name or --clear-environment",
|
|
749
|
+
);
|
|
734
750
|
const name = getFlag(ctx.args, "--name");
|
|
735
751
|
const prompt = getFlag(ctx.args, "--prompt");
|
|
736
752
|
const description = getFlag(ctx.args, "--description");
|
|
@@ -753,6 +769,7 @@ async function runUpdate(ctx: CronCliContext): Promise<void> {
|
|
|
753
769
|
const schedule = consumeScheduleFlags(ctx.args);
|
|
754
770
|
const result = await callMutation(ctx, updateCronTriggerRef, {
|
|
755
771
|
jobId,
|
|
772
|
+
environment: clearEnvironment ? null : environment,
|
|
756
773
|
name,
|
|
757
774
|
prompt,
|
|
758
775
|
description,
|
|
@@ -866,6 +883,8 @@ Browse:
|
|
|
866
883
|
dench cron events <toolkit> [--json] # app-event types for a connected app
|
|
867
884
|
|
|
868
885
|
Create / update:
|
|
886
|
+
Add --environment <name> to bind a ready reusable setup.
|
|
887
|
+
New runs use its latest ready revision; --clear-environment removes the binding on update.
|
|
869
888
|
dench cron create "Daily standup digest" \\
|
|
870
889
|
--prompt "Pull yesterday's commits + Slack #standup msgs and post a summary…" \\
|
|
871
890
|
--type schedule # default. also: webhook | app | crm (see below)
|