@visual-z/xeno 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 CHANGED
@@ -17,7 +17,7 @@ is ever summarized by a model, and nothing is ever rewritten in place.
17
17
  messages[] frozen system prefix + handoff + this node's events
18
18
  ```
19
19
 
20
- Version **0.1.0**. The on-disk session format will still change; `0.x` is that
20
+ Version **0.1.1**. The on-disk session format will still change; `0.x` is that
21
21
  statement. Published as `@visual-z/xeno`; the license is `UNLICENSED` (readable and installable, no reuse granted).
22
22
 
23
23
  ## Install
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visual-z/xeno",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "description": "Segmented append-only agent log with pure context and handoff projections",
@@ -125,12 +125,29 @@ export function cliSessionConfig(args: Args): { continuationAfter: number; timeB
125
125
  return { continuationAfter: Infinity, ...(args.timeBudgetMs === undefined ? {} : { timeBudgetMs: args.timeBudgetMs }) };
126
126
  }
127
127
 
128
+ /**
129
+ * Pure: where each kind of state lives for this workspace. Tool observations
130
+ * are not listed: Session keeps them in `<logDir>/artifacts`, inside the
131
+ * session, so fork and copy carry them -- moving sessions moves them too.
132
+ */
133
+ export function statePaths(workspace: string, stateDir?: string): { blobs: string; sessions: string } {
134
+ const root = stateDir ?? join(workspace, ".xeno");
135
+ return { blobs: join(root, "blobs"), sessions: join(root, "sessions") };
136
+ }
137
+
128
138
  export type AskSlot = {
129
139
  current?: (request: PermissionRequest) => Promise<ApprovalChoice>;
130
140
  };
131
141
 
132
142
  export interface Args {
133
143
  workspace: string;
144
+ /**
145
+ * `--state-dir <dir>`: where every xeno write goes -- snapshots, tool
146
+ * observations and, unless --session-dir says otherwise, sessions. Absent
147
+ * means `<workspace>/.xeno`. Evaluations need it outside the workspace:
148
+ * patches and verifiers see whatever is written there.
149
+ */
150
+ stateDir?: string;
134
151
  /** `--time-budget <seconds>`, in ms: when to warn the model its run is nearly out of time. */
135
152
  timeBudgetMs?: number;
136
153
  /**
@@ -213,6 +230,7 @@ export interface Args {
213
230
  const KNOWN_FLAGS: readonly string[] = [
214
231
  "--workspace", "-w",
215
232
  "--path-root",
233
+ "--state-dir",
216
234
  "--time-budget",
217
235
  "--log",
218
236
  "--max-turns",
@@ -346,6 +364,7 @@ export function parseArgs(argv: string[]): Args {
346
364
  const a = argv[i]!;
347
365
  if (a === "--workspace" || a === "-w") args.workspace = argv[++i] ?? args.workspace;
348
366
  else if (a === "--path-root") args.pathRoot = resolve(argv[++i] ?? "/");
367
+ else if (a === "--state-dir") args.stateDir = resolve(argv[++i] ?? ".");
349
368
  else if (a === "--time-budget") {
350
369
  const seconds = Number(argv[++i]);
351
370
  if (!Number.isFinite(seconds) || seconds <= 0) throw new Error("--time-budget requires a positive number of seconds");
@@ -447,6 +466,7 @@ export function parseArgs(argv: string[]): Args {
447
466
  " --no-builtin-tools drops built-ins and keeps any other source; --tools still wins.\n" +
448
467
  " bash asks before it runs. --allow-tools bash skips the ask for this process.\n" +
449
468
  " --time-budget <seconds> tells the model, once, when 75% of the run's wall-clock budget is used.\n" +
469
+ " --state-dir <dir> keeps snapshots, observations and sessions out of the workspace.\n" +
450
470
  " --path-root <dir> lets file tools reach absolute paths under <dir> (e.g. / in a container);\n" +
451
471
  " relative paths, fingerprints and /back stay in the workspace, so edits outside it are not rewound.\n" +
452
472
  " --approve loads .xeno/settings.json this run; --no-approve skips it.\n" +
@@ -1098,7 +1118,8 @@ export async function startup(args: Args): Promise<App> {
1098
1118
  * log stays where it is, still resumable by pointing --log at it. A leftover
1099
1119
  * `.xeno/log` from before named sessions is the same: pointed at, not moved.
1100
1120
  */
1101
- const sessionDir = (args.sessionDir ?? "").length > 0 ? args.sessionDir : undefined;
1121
+ const paths = statePaths(workspace, args.stateDir);
1122
+ const sessionDir = (args.sessionDir ?? "").length > 0 ? args.sessionDir : args.stateDir !== undefined ? paths.sessions : undefined;
1102
1123
  const sessionPick =
1103
1124
  (args.session ?? "").length > 0
1104
1125
  ? args.session
@@ -1147,7 +1168,7 @@ export async function startup(args: Args): Promise<App> {
1147
1168
  // read-only branch has nothing to snapshot, but a session a person drives is
1148
1169
  // exactly the one that will want to go back, and the content has to have been
1149
1170
  // stored before the turn a person wants back -- it cannot be opted into after.
1150
- const blobDir = join(workspace, ".xeno", "blobs");
1171
+ const blobDir = paths.blobs;
1151
1172
  await mkdir(logDir, { recursive: true });
1152
1173
  await mkdir(blobDir, { recursive: true });
1153
1174