Files
HologramAgent-DSH-Memory-Port/scripts/link-dsh.sh
jianf 6d9ad62a8f feat(memory): surface recall as a system section instead of history
Per-turn recall currently enters through the `agent/pre-step` waterfall, and
that hook can only return `messages`. The recall block therefore lands in
conversation history as a user message: the model reads its own memory as if a
user had said it. `systemPrompt.context` cannot fix this — it runs before the
turn text is known, so it can only render input-independent text.

The loop's `preStep` is the first seam holding BOTH the assembled prompt and
this turn's claimed messages. `lib/loop.js` subclasses the harness AgentLoop and
wraps that seam on the agent instance: a pure snapshot carrier message is moved
out of `messages` and into `assembly.sections`, so it becomes part of the system
prompt for that turn and never enters history.

Deliberately decoupled from the memory plugin: nothing imports it or matches its
name. Promotion keys off the shape every snapshot-style injector already uses
(`source.sections`, the same shape `dsh-time-context` emits).

Constraints honoured, each of which was a measured failure elsewhere:
- The promoted section carries `interpolate: false`; recalled text is free text
  and may contain `{{...}}`, which the strict interpolator rejects.
- The subclass defines no constructor and no static `inject`. The parent already
  registers the turnBoundary + inbox projections, calls `setFactory(this)`, and
  declares `{{provider}}`/`{{model}}`; re-registering any of them throws at
  startup.
- Only the instance's `preStep` is patched, never the prototype, which other
  agents share.

Also wires the new peer: `scripts/link-dsh.sh` gains dsh-agent/dsh-agent-loop
(without them the package cannot resolve at test time), package.json declares
dsh-agent-loop as a peer and adds the new test file, and G2b's allowlist admits
dsh-agent-loop as a first-party harness package, with the reason recorded.

npm test: 247 pass, 0 fail.
2026-11-04 14:54:49 +08:00

56 lines
1.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Link the globally-installed dsh packages into node_modules.
#
# The plugin declares no dependency of its own, so nothing here is fetched from a
# registry. The dsh packages it *imports* at runtime are **peers**: they must come
# from the running harness, and npm will not install them for us. During
# development they live in
# /usr/lib/node_modules/@deepseek-ai (a global install), so this script wires them
# up by symlink instead of vendoring a copy that could drift from the harness.
#
# Usage: ./scripts/link-dsh.sh [global-modules-root]
set -euo pipefail
GLOBAL_ROOT="${1:-/usr/lib/node_modules}"
PEERS=(
cordis
dsh-agent
dsh-agent-loop
cosmokit
dsh-llm
dsh-session
dsh-scope
dsh-system-prompt
dsh-tools
dsh-util-values
cordis-plugin-loader
)
cd "$(dirname "$0")/.."
if [[ ! -d "$GLOBAL_ROOT/@deepseek-ai" ]]; then
echo "error: $GLOBAL_ROOT/@deepseek-ai not found — pass the root that holds the dsh install" >&2
exit 1
fi
mkdir -p node_modules/@deepseek-ai
for peer in "${PEERS[@]}"; do
src="$GLOBAL_ROOT/@deepseek-ai/$peer"
if [[ ! -d "$src" ]]; then
echo " skip $peer (not installed)"
continue
fi
ln -sfn "$src" "node_modules/@deepseek-ai/$peer"
echo " link $peer"
done
# js-yaml is a transitive dep of cordis-plugin-include; needed only by the
# loader integration test, and only when it is not resolvable already.
if [[ -d "$GLOBAL_ROOT/js-yaml" && ! -e node_modules/js-yaml ]]; then
ln -sfn "$GLOBAL_ROOT/js-yaml" node_modules/js-yaml
echo " link js-yaml"
fi
echo
echo "done. try: npm test && npm run check"