@deepseek-ai/dsh-tmux-context 0.1.6-alpha.2 → 0.1.7-alpha.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/lib/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { z as z$1 } from "zod";
4
- import { Service } from "@deepseek-ai/cordis";
4
+ import { Context, Service } from "@deepseek-ai/cordis";
5
5
  //#region ../../typert/protocol/src/remote-error.ts
6
6
  /**
7
7
  * One Remote call failure: a real Error carrying its stable code and typed
@@ -47,7 +47,10 @@ function isTypertRemoteSegment(value) {
47
47
  }
48
48
  const REMOTE_METHOD_DESCRIPTOR = "@deepseek-ai/dsh-typert-protocol/remote-methods";
49
49
  /**
50
- * Bind one visible Service field to a Cordis key and Remote namespace.
50
+ * Bind one visible Service field to a Cordis key and Remote namespace. A
51
+ * service that owns a Cordis Context also gives its tree `ctx.invocation`,
52
+ * `undefined` outside a Remote call, so no `TypertRemoteService` is needed for
53
+ * a Host composition to read it.
51
54
  * @param service - owning Service instance, normally `this`.
52
55
  * @param serviceKey - exact Cordis service key.
53
56
  * @param options - optional distinct wire namespace.
@@ -57,6 +60,8 @@ function bindTypertRemote(service, serviceKey, options = {}) {
57
60
  validateName("service key", serviceKey);
58
61
  const namespace = options.namespace ?? serviceKey;
59
62
  validateName("namespace", namespace);
63
+ const ctx = Reflect.get(service, "ctx");
64
+ if (ctx instanceof Context) provideInvocationAccessor(ctx);
60
65
  return Object.freeze({
61
66
  service,
62
67
  serviceKey,
@@ -78,6 +83,16 @@ var TypertRemoteService = class extends Service {
78
83
  this.typertRemote = bindTypertRemote(this, this.name, options);
79
84
  }
80
85
  };
86
+ /**
87
+ * Make `ctx.invocation` read as `undefined` outside a Remote call instead of the
88
+ * reflect service's "cannot get property" error; a call-derived Context shadows
89
+ * the accessor with its own property. The first Remote Service constructed in a
90
+ * tree registers it on the root, where it outlives any one Service.
91
+ */
92
+ function provideInvocationAccessor(ctx) {
93
+ if (Object.hasOwn(ctx.root.reflect.props, "invocation")) return;
94
+ ctx.root.accessor("invocation", { get: () => void 0 });
95
+ }
81
96
  function Remote(methodExportOrOptions, context) {
82
97
  if (typeof methodExportOrOptions === "string") {
83
98
  validateName("Remote export name", methodExportOrOptions);
@@ -231,10 +246,10 @@ function freezeMessage(message) {
231
246
  * @returns an immutable message with a fresh stable identity.
232
247
  */
233
248
  function createMessage(input) {
234
- return freezeMessage({
249
+ return deepFreeze(structuredClone({
235
250
  ...input,
236
251
  id: brandString(randomUUID())
237
- });
252
+ }));
238
253
  }
239
254
  /**
240
255
  * Create one identified user-role message and freeze it before publication.
@@ -510,25 +525,23 @@ function textOnlyImageText(ref) {
510
525
  return `[image omitted because this model accepts text only; attachment sha256:${String(ref.attachmentId).slice(7, 15)}]`;
511
526
  }
512
527
  /**
513
- * True when typed model content contains an image block, walking nested
514
- * tool-result content. This is the one recursive image walk shared by every
515
- * image policy (capability gating, text-only serialization, compaction
516
- * survey), so a consumer cannot silently diverge on nesting depth.
528
+ * True when typed model content contains an image block. This is the one image
529
+ * walk shared by every image policy (capability gating, text-only
530
+ * serialization, compaction survey), so a consumer cannot silently diverge.
517
531
  * @param content - typed model content blocks.
518
- * @returns whether any nested block is an image.
532
+ * @returns whether any block is an image.
519
533
  */
520
534
  function contentHasImage(content) {
521
- return content.some((block) => block.type === "image" || block.type === "tool-result" && contentHasImage(block.content));
535
+ return content.some((block) => block.type === "image");
522
536
  }
523
537
  /**
524
- * True when typed model content contains a file block, walking nested
525
- * tool-result content on the same recursion every file policy shares.
538
+ * True when typed model content contains a file block.
526
539
  * Reads current content on every call without retaining scan results.
527
540
  * @param content - typed model content blocks.
528
- * @returns whether any nested block is a file.
541
+ * @returns whether any block is a file.
529
542
  */
530
543
  function contentHasFile(content) {
531
- for (const block of content) if (block.type === "file" || block.type === "tool-result" && contentHasFile(block.content)) return true;
544
+ for (const block of content) if (block.type === "file") return true;
532
545
  return false;
533
546
  }
534
547
  /**
@@ -545,7 +558,7 @@ function fileHandleText(ref, readonlyPath) {
545
558
  if (readonlyPath === void 0) return `[${identity} was uploaded, but the current execution environment cannot access a readable path. Report that limitation if its contents are needed; do not claim to have read it.]`;
546
559
  return `[${identity}: verbatim read-only copy saved at ${quoted(readonlyPath)}. Read that path with your file tools when its contents are needed; copy it to a writable location before modifying it. When delegating file work, include this saved path in the delegation prompt; only subagents sharing this execution environment can read it.]`;
547
560
  }
548
- /** Replace every file occurrence, including nested tool results, with handle text. */
561
+ /** Replace every file occurrence with handle text. */
549
562
  function replaceFilesWithHandles(blocks, resolvePath) {
550
563
  let next;
551
564
  for (const [index, block] of blocks.entries()) {
@@ -557,29 +570,10 @@ function replaceFilesWithHandles(blocks, resolvePath) {
557
570
  });
558
571
  continue;
559
572
  }
560
- if (block.type === "tool-result") {
561
- const content = replaceFilesWithHandles(block.content, resolvePath);
562
- if (content !== block.content) {
563
- next ??= blocks.slice(0, index);
564
- next.push({
565
- ...block,
566
- content
567
- });
568
- continue;
569
- }
570
- }
571
573
  next?.push(block);
572
574
  }
573
575
  return next ?? blocks;
574
576
  }
575
- /**
576
- * Project durable file history into deterministic handle text for every model
577
- * route. Unlike images, no provider receives file blocks natively, so this
578
- * projection is unconditional in request assembly.
579
- * @param messages - complete request history.
580
- * @param resolvePath - resolve one reference's current execution-world read path.
581
- * @returns the original list without files, otherwise shallow message copies with handle text.
582
- */
583
577
  function projectFilesToText(messages, resolvePath) {
584
578
  if (!messages.some((message) => contentHasFile(message.content))) return messages;
585
579
  return messages.map((message) => {
@@ -590,7 +584,7 @@ function projectFilesToText(messages, resolvePath) {
590
584
  };
591
585
  });
592
586
  }
593
- /** Replace every image occurrence, including nested tool results, for a text-only model. */
587
+ /** Replace every image occurrence for a text-only model. */
594
588
  function replaceImagesForTextModel(blocks) {
595
589
  let next;
596
590
  for (const [index, block] of blocks.entries()) {
@@ -602,26 +596,10 @@ function replaceImagesForTextModel(blocks) {
602
596
  });
603
597
  continue;
604
598
  }
605
- if (block.type === "tool-result") {
606
- const content = replaceImagesForTextModel(block.content);
607
- if (content !== block.content) {
608
- next ??= blocks.slice(0, index);
609
- next.push({
610
- ...block,
611
- content
612
- });
613
- continue;
614
- }
615
- }
616
599
  next?.push(block);
617
600
  }
618
601
  return next ?? blocks;
619
602
  }
620
- /**
621
- * Project durable image history into deterministic text for an exact text-only model.
622
- * @param messages - complete request history.
623
- * @returns the original list without images, otherwise shallow message copies with stable placeholders.
624
- */
625
603
  function projectImagesForTextModel(messages) {
626
604
  if (!messages.some((message) => contentHasImage(message.content))) return messages;
627
605
  return messages.map((message) => {
@@ -1214,8 +1192,9 @@ var LlmError = class extends HarnessError {
1214
1192
  /** Remove replay state whose historical route is owned by another adapter. */
1215
1193
  forAdapter(options, adapter) {
1216
1194
  const messages = options.messages.map((message) => {
1195
+ if (message.role !== "assistant") return message;
1217
1196
  const source = message.source;
1218
- if (message.role !== "assistant" || source.kind !== "model" || source.replayState === void 0) return message;
1197
+ if (source.replayState === void 0) return message;
1219
1198
  if (this.adapters.get(source.provider)?.adapter === adapter) return message;
1220
1199
  return freezeMessage({
1221
1200
  ...message,
@@ -1441,10 +1420,10 @@ async function queryTmuxLocation(bash, logger, processId, signal) {
1441
1420
  ].join("\n");
1442
1421
  let result;
1443
1422
  try {
1444
- result = await bash.run(bash.resolve({
1423
+ result = await (await bash.execute(bash.resolve({
1445
1424
  command,
1446
1425
  signal
1447
- }));
1426
+ }))).result();
1448
1427
  } catch (error) {
1449
1428
  const message = error instanceof Error ? error.message : String(error);
1450
1429
  logger.warn(`tmux location query failed: ${message}; injecting no location this turn`);
@@ -1501,7 +1480,7 @@ function apply(ctx, config) {
1501
1480
  stateSchema: tmuxContextStateSchema,
1502
1481
  init: () => null,
1503
1482
  apply: (state, event) => {
1504
- if (event.type !== "user/message" || event.data.source.kind !== "plugin" || event.data.source.plugin !== "tmux-context") return state;
1483
+ if (event.type !== "user/message" || event.data.source.kind !== "tmux-context") return state;
1505
1484
  const [block] = event.data.content;
1506
1485
  if (block?.type !== "text") return state;
1507
1486
  const newline = block.text.indexOf("\n");
@@ -1534,8 +1513,7 @@ function apply(ctx, config) {
1534
1513
  text
1535
1514
  }],
1536
1515
  source: {
1537
- kind: "plugin",
1538
- plugin: name,
1516
+ kind: name,
1539
1517
  form: "snapshot",
1540
1518
  sections: [{
1541
1519
  name,
@@ -20,6 +20,18 @@
20
20
  import type { Context } from '@deepseek-ai/cordis';
21
21
  import z from '@deepseek-ai/schemastery';
22
22
  import { z as zod } from 'zod';
23
+ import type { ContextFormed } from '@deepseek-ai/dsh-llm';
24
+ declare module '@deepseek-ai/dsh-llm' {
25
+ interface MessageSourceMap {
26
+ /** Location attribution; readers preserve the content without this producer.
27
+ * Its projection uses the kind to avoid repeated injection.
28
+ * @persistenceAttribution
29
+ */
30
+ 'tmux-context': {
31
+ kind: 'tmux-context';
32
+ } & ContextFormed;
33
+ }
34
+ }
23
35
  /** Cordis plugin name used by loader diagnostics. */
24
36
  export declare const name = "tmux-context";
25
37
  /** The agent registry that owns pre-step processing. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-tmux-context",
3
3
  "description": "Opt-in durable per-step context with this agent's tmux pane and window location",
4
- "version": "0.1.6-alpha.2",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -28,23 +28,23 @@
28
28
  "license": "MIT",
29
29
  "dependencies": {
30
30
  "zod": "^4.4.3",
31
- "@deepseek-ai/schemastery": "^3.18.2"
31
+ "@deepseek-ai/schemastery": "^3.18.3"
32
32
  },
33
33
  "peerDependencies": {
34
- "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
35
- "@deepseek-ai/dsh-shell": "^0.1.6-alpha.2",
36
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
37
- "@deepseek-ai/dsh-session-projection": "^0.1.6-alpha.2",
38
- "@deepseek-ai/cordis": "^4.0.2"
34
+ "@deepseek-ai/dsh-agent": "^0.1.7-alpha.1",
35
+ "@deepseek-ai/dsh-shell": "^0.1.7-alpha.1",
36
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
37
+ "@deepseek-ai/cordis": "^4.0.3",
38
+ "@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@deepseek-ai/dsh-agent": "^0.1.6-alpha.2",
42
- "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.6-alpha.2",
43
- "@deepseek-ai/dsh-shell": "^0.1.6-alpha.2",
44
- "@deepseek-ai/dsh-llm": "^0.1.6-alpha.2",
45
- "@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
46
- "@deepseek-ai/dsh-session-projection": "^0.1.6-alpha.2",
47
- "@deepseek-ai/cordis": "^4.0.2",
48
- "@deepseek-ai/dsh-system-prompt": "^0.1.6-alpha.2"
41
+ "@deepseek-ai/dsh-agent": "^0.1.7-alpha.1",
42
+ "@deepseek-ai/dsh-shell": "^0.1.7-alpha.1",
43
+ "@deepseek-ai/dsh-llm": "^0.1.7-alpha.1",
44
+ "@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
45
+ "@deepseek-ai/cordis": "^4.0.3",
46
+ "@deepseek-ai/dsh-system-prompt": "^0.1.7-alpha.1",
47
+ "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.7-alpha.1",
48
+ "@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1"
49
49
  }
50
50
  }