cutroom 0.1.0__tar.gz
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.
- cutroom-0.1.0/.github/workflows/bench.yml +43 -0
- cutroom-0.1.0/.github/workflows/ci.yml +35 -0
- cutroom-0.1.0/.github/workflows/release.yml +31 -0
- cutroom-0.1.0/.gitignore +20 -0
- cutroom-0.1.0/.python-version +1 -0
- cutroom-0.1.0/AGENTS.md +1 -0
- cutroom-0.1.0/BACKLOG.md +193 -0
- cutroom-0.1.0/CLAUDE.md +41 -0
- cutroom-0.1.0/DESIGN.md +107 -0
- cutroom-0.1.0/LICENSE +21 -0
- cutroom-0.1.0/PKG-INFO +301 -0
- cutroom-0.1.0/README.md +277 -0
- cutroom-0.1.0/bench/repurpose.json +25 -0
- cutroom-0.1.0/docs/ablation-ea1da55fb38d.json +47 -0
- cutroom-0.1.0/docs/agent-paradigms.md +59 -0
- cutroom-0.1.0/docs/bench-repurpose-5a876fe0d613.json +72 -0
- cutroom-0.1.0/docs/demo-captioned-frame.jpg +0 -0
- cutroom-0.1.0/docs/demo-vertical-frame.jpg +0 -0
- cutroom-0.1.0/docs/demo.gif +0 -0
- cutroom-0.1.0/pyproject.toml +61 -0
- cutroom-0.1.0/scripts/ablation.py +93 -0
- cutroom-0.1.0/scripts/bench_repurpose.py +61 -0
- cutroom-0.1.0/scripts/record_demo.sh +26 -0
- cutroom-0.1.0/src/cutroom/__init__.py +3 -0
- cutroom-0.1.0/src/cutroom/agent/__init__.py +25 -0
- cutroom-0.1.0/src/cutroom/agent/budget.py +38 -0
- cutroom-0.1.0/src/cutroom/agent/fanout.py +150 -0
- cutroom-0.1.0/src/cutroom/agent/hooks.py +229 -0
- cutroom-0.1.0/src/cutroom/agent/prompts.py +111 -0
- cutroom-0.1.0/src/cutroom/agent/runner.py +331 -0
- cutroom-0.1.0/src/cutroom/agent/tools.py +556 -0
- cutroom-0.1.0/src/cutroom/bench.py +149 -0
- cutroom-0.1.0/src/cutroom/checkpoints.py +143 -0
- cutroom-0.1.0/src/cutroom/cli.py +772 -0
- cutroom-0.1.0/src/cutroom/db.py +308 -0
- cutroom-0.1.0/src/cutroom/ffmpeg_util.py +91 -0
- cutroom-0.1.0/src/cutroom/index/__init__.py +2 -0
- cutroom-0.1.0/src/cutroom/index/map.py +184 -0
- cutroom-0.1.0/src/cutroom/index/search.py +66 -0
- cutroom-0.1.0/src/cutroom/ingest/__init__.py +12 -0
- cutroom-0.1.0/src/cutroom/ingest/asr.py +49 -0
- cutroom-0.1.0/src/cutroom/ingest/audio.py +83 -0
- cutroom-0.1.0/src/cutroom/ingest/fetch.py +134 -0
- cutroom-0.1.0/src/cutroom/ingest/logger.py +53 -0
- cutroom-0.1.0/src/cutroom/ingest/shots.py +61 -0
- cutroom-0.1.0/src/cutroom/recipes/__init__.py +134 -0
- cutroom-0.1.0/src/cutroom/recipes/builtin/podcast-shorts.md +11 -0
- cutroom-0.1.0/src/cutroom/recipes/builtin/quotes.md +10 -0
- cutroom-0.1.0/src/cutroom/recipes/builtin/talk-highlights.md +9 -0
- cutroom-0.1.0/src/cutroom/recipes/builtin/teaser.md +10 -0
- cutroom-0.1.0/src/cutroom/recipes/builtin/tighten.md +10 -0
- cutroom-0.1.0/src/cutroom/render/__init__.py +6 -0
- cutroom-0.1.0/src/cutroom/render/captions.py +108 -0
- cutroom-0.1.0/src/cutroom/render/edl.py +92 -0
- cutroom-0.1.0/src/cutroom/render/ffmpeg.py +79 -0
- cutroom-0.1.0/src/cutroom/render/receipts.py +99 -0
- cutroom-0.1.0/src/cutroom/sessions.py +97 -0
- cutroom-0.1.0/src/cutroom/trail.py +82 -0
- cutroom-0.1.0/src/cutroom/types.py +144 -0
- cutroom-0.1.0/tests/conftest.py +132 -0
- cutroom-0.1.0/tests/test_agent_tools.py +305 -0
- cutroom-0.1.0/tests/test_bench.py +111 -0
- cutroom-0.1.0/tests/test_budget.py +55 -0
- cutroom-0.1.0/tests/test_captions.py +102 -0
- cutroom-0.1.0/tests/test_checkpoints.py +206 -0
- cutroom-0.1.0/tests/test_cli.py +129 -0
- cutroom-0.1.0/tests/test_e2e.py +41 -0
- cutroom-0.1.0/tests/test_edl.py +165 -0
- cutroom-0.1.0/tests/test_fanout.py +99 -0
- cutroom-0.1.0/tests/test_ffmpeg_util.py +69 -0
- cutroom-0.1.0/tests/test_hooks.py +197 -0
- cutroom-0.1.0/tests/test_ingest.py +130 -0
- cutroom-0.1.0/tests/test_map.py +75 -0
- cutroom-0.1.0/tests/test_recipes.py +143 -0
- cutroom-0.1.0/tests/test_render.py +109 -0
- cutroom-0.1.0/tests/test_search.py +63 -0
- cutroom-0.1.0/tests/test_sessions.py +131 -0
- cutroom-0.1.0/tests/test_steering.py +131 -0
- cutroom-0.1.0/tests/test_trail.py +102 -0
- cutroom-0.1.0/tests/test_verify.py +156 -0
- cutroom-0.1.0/uv.lock +1523 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: bench
|
|
2
|
+
|
|
3
|
+
# Manual-only: real agent runs cost real tokens. Needs the ANTHROPIC_API_KEY secret.
|
|
4
|
+
# Downloads a public-domain film, logs it (CPU whisper), runs the repurpose
|
|
5
|
+
# scorecard, and publishes the table to the run's step summary + an artifact.
|
|
6
|
+
on:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
video_url:
|
|
10
|
+
description: "video to bench against"
|
|
11
|
+
default: "https://archive.org/download/DuckandC1951/DuckandC1951_512kb.mp4"
|
|
12
|
+
whisper_model:
|
|
13
|
+
description: "faster-whisper model size"
|
|
14
|
+
default: "small"
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
repurpose:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 60
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
- uses: actions/cache@v5
|
|
26
|
+
with:
|
|
27
|
+
path: ~/.cache/huggingface
|
|
28
|
+
key: hf-whisper-${{ inputs.whisper_model }}-${{ runner.os }}
|
|
29
|
+
- name: Install ffmpeg + yt-dlp
|
|
30
|
+
run: sudo apt-get update && sudo apt-get install -y ffmpeg && uv tool install yt-dlp
|
|
31
|
+
- run: uv sync
|
|
32
|
+
- name: Log the footage
|
|
33
|
+
run: uv run cutroom log "${{ inputs.video_url }}" --whisper-model "${{ inputs.whisper_model }}"
|
|
34
|
+
- name: Run the repurpose scorecard
|
|
35
|
+
env:
|
|
36
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
37
|
+
run: |
|
|
38
|
+
VID=$(uv run cutroom list --ids | head -1)
|
|
39
|
+
uv run python scripts/bench_repurpose.py "$VID"
|
|
40
|
+
- uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: bench-repurpose
|
|
43
|
+
path: docs/bench-repurpose-*.json
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
# Whisper test model: cached because anonymous Hub downloads from shared
|
|
22
|
+
# runners are 429-rate-limited; on a cache miss + 429 the ASR tests skip.
|
|
23
|
+
- uses: actions/cache@v5
|
|
24
|
+
with:
|
|
25
|
+
path: ~/.cache/huggingface
|
|
26
|
+
key: hf-whisper-tiny-${{ runner.os }}
|
|
27
|
+
- name: Install ffmpeg
|
|
28
|
+
run: |
|
|
29
|
+
if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get update && sudo apt-get install -y ffmpeg
|
|
30
|
+
else brew install ffmpeg; fi
|
|
31
|
+
- run: uv sync
|
|
32
|
+
- run: uv run ruff check src tests
|
|
33
|
+
# `say` is unavailable on CI runners — the synthetic fixture degrades to tones
|
|
34
|
+
# and speech-dependent assertions skip automatically.
|
|
35
|
+
- run: uv run pytest -q -m "not requires_claude"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Manual-only: publishes the current ref to PyPI.
|
|
4
|
+
# Auth, either of:
|
|
5
|
+
# - PyPI trusted publishing configured for this repo/workflow (preferred, no secret), or
|
|
6
|
+
# - a PYPI_API_TOKEN repository secret.
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
pypi:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write # trusted publishing (OIDC)
|
|
16
|
+
contents: read
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v6
|
|
19
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- run: uv sync
|
|
23
|
+
- run: uv run ruff check src tests
|
|
24
|
+
- run: uv run pytest -q -m "not requires_claude"
|
|
25
|
+
- run: uv build
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
env:
|
|
28
|
+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
|
29
|
+
run: |
|
|
30
|
+
if [ -n "$UV_PUBLISH_TOKEN" ]; then uv publish
|
|
31
|
+
else uv publish --trusted-publishing always; fi
|
cutroom-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Tooling caches
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
|
|
16
|
+
# Local workspace artifacts
|
|
17
|
+
.cutroom/
|
|
18
|
+
renders/
|
|
19
|
+
.DS_Store
|
|
20
|
+
dist/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
cutroom-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
CLAUDE.md
|
cutroom-0.1.0/BACKLOG.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# cutroom — 迭代 backlog
|
|
2
|
+
|
|
3
|
+
> 工作协议(2026-06-11 用户指示改为全自动连续模式):按优先级连续执行,不人为间隔,
|
|
4
|
+
> 直到 Claude 判断项目达到"好项目"标准:全部动词真实验证 / 测试全绿 / README 带真实
|
|
5
|
+
> 证据 / 发布就绪 / 索引优先架构有量化消融证据。每项完成移入「已完成」附一行结果。
|
|
6
|
+
> 命中用量限制时安排自动唤醒续跑。不 commit、不 push(仍需用户明确授权)。
|
|
7
|
+
|
|
8
|
+
## 下一步(按优先级)
|
|
9
|
+
|
|
10
|
+
经 2026-06 完整 deep research(见 docs/agent-paradigms.md)排出的范式吸收优先级:
|
|
11
|
+
|
|
12
|
+
1. **PyPI 发包(唯一剩余,等用户侧动作)**:在 pypi.org 给 repo 配 trusted publishing
|
|
13
|
+
(publisher: GitHub / Young-1231/cutroom / release.yml / environment: pypi),或把
|
|
14
|
+
PYPI_API_TOKEN 加进 repo secrets;然后手动触发 `release` workflow 即发布。
|
|
15
|
+
发包成功后:README Install 节加 `uv tool install cutroom` 一行。
|
|
16
|
+
2. 传播:拿 demo GIF 发 HN (Show HN) / r/LocalLLaMA / X(用户自行决定时机与文案)。
|
|
17
|
+
|
|
18
|
+
(2026-06 范式吸收 backlog 已全部清空;2026-06-12 已上线:仓库 public、v0.1.0 tag +
|
|
19
|
+
GitHub Release(带 wheel/sdist)、release.yml 一键发包 workflow、README badges。)
|
|
20
|
+
|
|
21
|
+
## 已完成
|
|
22
|
+
|
|
23
|
+
- 发布产品化(2026-06-12):README 大改版(hero demo GIF + 8 特性段 + FAQ + 跑分表 +
|
|
24
|
+
状态更新);docs/demo.gif —— asciinema 录**真实运行**(list → cut --plan 进度行全程
|
|
25
|
+
→ checkpoints,agent 自己 load_recipe(teaser) 出镜)3.5× 加速 19s/438KB,
|
|
26
|
+
scripts/record_demo.sh 可复录;PyPI 就绪(classifiers/keywords/urls,uv build +
|
|
27
|
+
twine check 双过,wheel 含内置 recipes);GitHub topics ×10。
|
|
28
|
+
|
|
29
|
+
- M2 Repurpose 跑分 + CI(2026-06-12):184 离线测试(bench +8)+ 真实跑分。cutroom.bench
|
|
30
|
+
只测机械可证伪项(EDL 产出 / 时长±tol / 刀数 / receipts 全覆盖 / 剪点距自然边界
|
|
31
|
+
≤0.5s),明确不做 LLM 评审(docstring 写明边界);bench/repurpose.json 3 任务规格;
|
|
32
|
+
scripts/bench_repurpose.py 出 markdown 表 + docs/bench-*.json + GITHUB_STEP_SUMMARY;
|
|
33
|
+
bench.yml workflow_dispatch(下载公有领域影片 → log → 跑分 → artifact,需
|
|
34
|
+
ANTHROPIC_API_KEY secret);`cutroom list --ids` 供脚本取 id。真实跑分:demo 影片
|
|
35
|
+
3/3 全过(12-21k chars / 16-22 轮),表格进 README。
|
|
36
|
+
|
|
37
|
+
- Checkpoint 三粒度 restore(2026-06-12,范式吸收 #2 收尾):184 离线测试(+4)+ 真实
|
|
38
|
+
验证。`cutroom restore --scope edl|session|both`:EDL 粒度即原行为(pre-restore 快照
|
|
39
|
+
保证可撤销);session 粒度经 checkpoint 记录的 session id(resolve 验证存在)给出
|
|
40
|
+
resume/fork 句柄——SDK 会话 append-only,"恢复会话"=从会话末尾续/分叉(agent
|
|
41
|
+
checkpoint 的会话末尾≈EDL 接受时刻,docstring 诚实记录该近似);both 二者兼做。
|
|
42
|
+
非 agent 来源(plan/render/pre-restore)的 checkpoint 申请 session 粒度 → 友好报错。
|
|
43
|
+
真实验证:--scope both 恢复 cp_0013 并给出当时 steering 会话句柄;plan checkpoint 走
|
|
44
|
+
session 粒度正确拒绝。
|
|
45
|
+
|
|
46
|
+
- Observability:`cutroom trail` CLI(2026-06-12,自研机制 #3,"无标准机制"三连收官):
|
|
47
|
+
173 离线测试(+7)+ 真实数据验证。trail.py 解析聚合(损坏行跳过不致命;fan-out 多
|
|
48
|
+
会话交错按 session 分组),CLI 三视图:默认每会话汇总表(calls/spent/denied/errors/
|
|
49
|
+
moments/edl)、--session 逐调用时间线(计费/余额、deny 黄色、EDL accepted → checkpoint
|
|
50
|
+
id、stop 含 breakdown)、--denials 跨会话门拒绝审计。真实验证:对真实 trail 钻取出
|
|
51
|
+
此前对抗测试的 sandbox deny 与逐调用账目。
|
|
52
|
+
|
|
53
|
+
- Verification/self-critique 回合(2026-06-12,自研机制 #2):166 离线测试(+10)+ 真实
|
|
54
|
+
e2e。`--verify`:EDL 落地后开**全新上下文** critic 会话(fresh eyes 而非窗口内自评,
|
|
55
|
+
role="critic" 工具面没有 propose_edl/mark_moment/load_recipe,只能查证据),逐刀读
|
|
56
|
+
边界转写 + 看刀内帧,经新工具 submit_review 提交结构化裁决(free、可在预算耗尽后
|
|
57
|
+
finalize);有 flag → 恰一轮修订,resume 回编辑器会话(receipts 延续),修订失败保
|
|
58
|
+
原 EDL。真实验证:critic 13→4 轮查证后 "verify ✓ all 1 cuts confirmed",理由具体到
|
|
59
|
+
段边界与帧时间戳。flagged→修订→保底三路径离线覆盖。
|
|
60
|
+
|
|
61
|
+
- 中途 steering + 工具调用进度行(2026-06-12,自研机制 #1):156 离线测试(+6)+ 真实
|
|
62
|
+
e2e。自研设计(调研确认无工业标准可抄):`--steer` 下 stdin 每行 → client.interrupt()
|
|
63
|
+
→ 驱动循环把该行包成 [USER STEERING] 重新 query 注入同一会话;prompt 字符串是唯一
|
|
64
|
+
通道,receipts 状态原样延续;只有最后一轮决定 ok/error(被打断≠失败)。runner 重构出
|
|
65
|
+
可测的 _drive_session(注入 client,FakeClient 离线全覆盖)。配套:每次工具调用打一行
|
|
66
|
+
紧凑进度(→ view_frames 42s,46.5s),有得看才有得 steer。真实验证:任务要 3 刀 30s
|
|
67
|
+
teaser,管道注入"只要 1 刀 Bert 歌 ≤15s"→ 最终 EDL 恰 1 刀 14.4s Bert 歌。
|
|
68
|
+
|
|
69
|
+
- 文件 allowlist sandbox(2026-06-12,范式吸收 #5b,Bundle 收官):150 离线测试(+6)
|
|
70
|
+
+ 对抗性真实 e2e。结论:SDK 的 OS 级 sandbox 只罩 Bash(cutroom 已三层拒绝 Bash),
|
|
71
|
+
对本工具面无增益且 Linux 依赖 bubblewrap 有破坏风险 → 正确落点是 hooks 层:
|
|
72
|
+
PreToolUse 给内置 Read(编辑器唯一碰文件系统的工具)加路径白名单,只许读本视频
|
|
73
|
+
media 目录;symlink resolve 后判定,相对路径直接拒(进程间 cwd 不一致)。
|
|
74
|
+
真实验证:诱导 agent 读 /tmp 探针文件 → 真实链路 deny + trail 记录 + 会话优雅继续,
|
|
75
|
+
秘密未泄露。这补上了间接注入经 Read 外读任意主机文件的最后一条路。
|
|
76
|
+
2026-06-12 范式吸收 Bundle(#5)至此全部落地:AGENTS.md / recipes→Skills / 文件沙箱。
|
|
77
|
+
|
|
78
|
+
- Recipes → Skills progressive disclosure(2026-06-12,范式吸收 #5a):144 离线测试(+15)
|
|
79
|
+
+ 真实 e2e。recipe 变成 SKILL.md 式文件(frontmatter: summary/vertical/reel/budget/n,
|
|
80
|
+
正文=专家指导):内置 5 个迁到 recipes/builtin/*.md,用户把 .md 丢进
|
|
81
|
+
$CUTROOM_HOME/recipes/ 即生效(按文件名覆盖内置)。双模式调用:CLI 显式 +
|
|
82
|
+
模型自调用——系统提示只进 name:summary 行,新 load_recipe 工具按需取正文(计费、
|
|
83
|
+
预算耗尽被 hooks 拒);scout 不带配方层。真实验证:teaser 任务首轮即
|
|
84
|
+
load_recipe(298 chars),按配方出 3 刀 hook→build→cliffhanger 结构 plan。
|
|
85
|
+
注意到内层 CLI 有 ToolSearch 轮次开销(18 次/23 轮),待查能否禁用。
|
|
86
|
+
|
|
87
|
+
- 强化 fan-out scout 隔离(2026-06-12,范式吸收 #4):117 离线测试(+2)+ 真实 fanout
|
|
88
|
+
e2e。scout 以 role="scout" 运行,make_toolkit `exclude` 把 propose_edl 从 MCP server
|
|
89
|
+
整体剥离(不进上下文,从 prompt 自律升级为代码强制);只有编排器能组 EDL。session
|
|
90
|
+
索引带 role,`cutroom sessions` 标记 scout 行。真实验证:2 窗并发(26,308 chars/24 轮
|
|
91
|
+
汇总),trail 显示 scout 工具集无 propose_edl,4 候选合并 top-2 落 EDL,receipts 契约保持。
|
|
92
|
+
|
|
93
|
+
- JSONL 会话持久化 + resume/fork(2026-06-12,范式吸收 #3):115 离线测试(+7)+ 真实
|
|
94
|
+
e2e 三连验证。fork 杀手锏的量化证明:父会话调查 12,489 chars/13 轮 → `--fork` 重剪
|
|
95
|
+
~10s teaser 只花 **1,500 chars/4 轮**(恰一次 view_frames),新 EDL 引用 [42.0, 46.0]
|
|
96
|
+
其中 42.0 直接复用父会话凭证(fork 中未重看,证据门凭重建状态放行)。
|
|
97
|
+
- **sessions.py**:sessions/index.jsonl(每 run 一条:task/spend/turns/lineage,同 id
|
|
98
|
+
resume 原位更新)+ sessions/<id>.json 证据状态(viewed_frames 跨 resume/fork 重建,
|
|
99
|
+
证据门继续认账,不强迫重看)。
|
|
100
|
+
- **runner**:`resume`/`fork` 参数 → SDK options.resume/fork_session;cwd 钉到
|
|
101
|
+
workspace home(会话 JSONL 按 cwd 派生目录存储,钉死后任意调用位置都能 resume);
|
|
102
|
+
EditorResult 带 session_id。
|
|
103
|
+
- **CLI**:`cutroom sessions <video>`(lineage 列:resumed / fork of);ask/cut 加
|
|
104
|
+
`--resume/--fork`(id 前缀解析、互斥校验);每次运行尾行打印 session 句柄。
|
|
105
|
+
- 真实验证:resume 同 id 续会话且逐字记得首问(0 chars/1 轮);fork 出新 id 带完整
|
|
106
|
+
父上下文;state 文件正确累积(父 6 帧 → fork 7 帧)。
|
|
107
|
+
|
|
108
|
+
- Shadow-VCS checkpoint over EDL(2026-06-12,范式吸收 #2):108 离线测试(+10)+ 真实
|
|
109
|
+
全闭环 e2e 验证(plan → 手改 → diff → restore → undo → render)。
|
|
110
|
+
- **checkpoints.py**:不依赖 git 的内容去重快照(HEAD 语义)+ cut 级语义 diff
|
|
111
|
+
(`~ cut 0 [68.46-87.82] -> [68.46-81.82]`,比行 diff 更贴 EDL);restore 先把当前态
|
|
112
|
+
存为 pre-restore checkpoint(restore 本身可撤销),损坏的 edl.json 移到 .corrupt 不覆盖。
|
|
113
|
+
- **四个落点**:agent propose_edl 被接受(经 hooks `on_edl_accepted` 挂载点,checkpoint id
|
|
114
|
+
写回 trail)、plan 保存、render 前(人工编辑由此进历史)、restore 前。
|
|
115
|
+
- **CLI**:`cutroom checkpoints <video>`(列表 + `--diff <cp>` 对比当前)、
|
|
116
|
+
`cutroom restore <video> <cp>`。
|
|
117
|
+
- 真实验证:demo 影片 plan 模式 12 轮落 EDL → cp_0001(agent)+cp_0002(plan,吸附差异被
|
|
118
|
+
正确识别为新状态) → 手剪 3s 后 diff 输出精确两行 → restore 自动存 cp_0003(pre-restore)
|
|
119
|
+
→ 从恢复态真实渲出 bert_01.mp4。Cline 式 task 粒度 restore 等 resume/fork 落地后升级。
|
|
120
|
+
|
|
121
|
+
- Hooks/生命周期门(2026-06-12,范式吸收 #1):98 离线测试(+11)+ 真实双 e2e 全绿。
|
|
122
|
+
- **agent/hooks.py**:`make_lifecycle_hooks(ledger, registry, trail_path)` 挂
|
|
123
|
+
PreToolUse/PostToolUse/PostToolUseFailure/Stop 四个 SDK hook 进 ClaudeAgentOptions。
|
|
124
|
+
- **PreToolUse 硬门**(只 deny 或沉默,allow 仍归 can_use_tool,参数报错仍归 handler):
|
|
125
|
+
① 副作用内置工具机械 deny(第三层);② 预算耗尽时 deny 调查类工具、finalize 仍放行;
|
|
126
|
+
③ 证据门——propose_edl/mark_moment 引用未真正 view 过的帧直接 deny。证据门控从
|
|
127
|
+
handler 自律升级为生命周期层结构性强制。
|
|
128
|
+
- **trail.jsonl 审计轨迹**(renders/ 下逐 JSON 行):每次工具调用的逐调用计费/余额、
|
|
129
|
+
deny 事件、错误、Stop 会话摘要(breakdown);`edl_accepted` 记录是 shadow-VCS
|
|
130
|
+
checkpoint 的挂载点。真实 ask 验证逐调用计费与 ledger 总额完全对账(4,123 chars)。
|
|
131
|
+
- **对抗性 e2e**:诱导 agent 不看帧直接 propose_edl → 真实 CLI 链路里 PreToolUse 门
|
|
132
|
+
deny,拒绝理由逐字回到模型,EDL 未注册,trail 记录 deny 事件。
|
|
133
|
+
- 顺手修:disallowed_tools 里的 MultiEdit 死规则(新 CLI 已并入 Edit,每次会话都警告)。
|
|
134
|
+
|
|
135
|
+
- harness 范式三连(2026-06-11,对标 Claude Code/OpenClaw 热点):99 离线测试 + 真实
|
|
136
|
+
e2e 全绿,demo 影片三链路 live 验证通过。
|
|
137
|
+
- **Plan mode(人在环)**:highlights/cut/recipe 加 `--plan` → 出可读剪辑方案
|
|
138
|
+
(时间码+理由+引用)+ 存 edl.json + 停,用户编辑后 `cutroom render` 落地。
|
|
139
|
+
- **Recipes(可复用专家配方)**:`cutroom recipes` 列表 + `cutroom recipe <name> <video>`;
|
|
140
|
+
内置 podcast-shorts/talk-highlights/teaser/quotes/tighten;对标 Claude Code skills。
|
|
141
|
+
- **Subagent fan-out**:`highlights --fanout` 按场景分窗 → 每窗一个 scout agent 并发
|
|
142
|
+
(CapacityLimiter 限流)→ 标记带分数的 moment → 全局去重排序 → 合并成 EDL;
|
|
143
|
+
moment 已过 view-frame 校验,receipts 契约保持。real demo: 2 窗并发 → 合并 2 刀。
|
|
144
|
+
|
|
145
|
+
- 多代理 code review 修复(2026-06-11):修掉 15 条审查发现的高优问题,85 离线测试
|
|
146
|
+
(+18 回归)+ 真实 e2e 全绿,真实影片 ask/highlights 端到端复验通过。要点:
|
|
147
|
+
- **安全**:内层 agent 从 bypassPermissions 改为 default + 白名单 can_use_tool 门
|
|
148
|
+
+ disallowed_tools,封堵恶意转写经间接注入触达 Bash/Write/WebFetch 的 RCE 链。
|
|
149
|
+
- **契约**:propose_edl 现强制 frame_ts 必须真的 view 过 + 非空 evidence(_basic_validate
|
|
150
|
+
同步),"每一刀有证据"不再可绕过。
|
|
151
|
+
- **健壮性**:ingest 幂等(replace_shots/segments/audio_events);ffmpeg 统一走
|
|
152
|
+
cutroom.ffmpeg_util(返回码检查 + latin-1 解码 + 单一 resolve_ffmpeg);shots 单切点
|
|
153
|
+
不再丢弃;read_span 续读点修正(不跳过半展示段)+ tiny-cap 不再死循环;snap_edl 收
|
|
154
|
+
真实 duration、防反转、丢退化 cut,CLI 侧重新校验;FTS 补 UPDATE 触发器;ASS 转义反斜杠/CR。
|
|
155
|
+
- **CLI**:友好错误边界(@friendly),agent 输出 markup=False(保留 [seg]/[mm:ss] 引用),
|
|
156
|
+
runner 暴露 ok/error(max_turns/API 错误不再伪装成正常结果),render 解析坏 json 不再 traceback。
|
|
157
|
+
- **清理**:删 3 个重复 mm:ss / 5 处 ffmpeg 封装合一 / receipts 复用已抽帧。
|
|
158
|
+
|
|
159
|
+
- 发布(2026-06-11,用户授权):private 仓库 https://github.com/Young-1231/cutroom,
|
|
160
|
+
2 commits(M0 + CI 修复),CI ubuntu+macos 双绿。CI 修复:HF 对共享 runner 匿名
|
|
161
|
+
下载限流 429 → whisper_tiny fixture 优雅 skip + actions/cache 缓存模型 + fail-fast 关闭。
|
|
162
|
+
|
|
163
|
+
- 预算消融实验(2026-06-11,全自动模式收官):His Girl Friday(92min,转写 133k
|
|
164
|
+
chars)3 问对照——cutroom 15.8-28k chars vs baseline 133.6k chars(4.8-8.5×),
|
|
165
|
+
双方全对、cutroom 附帧验证;结果进 README + docs/ablation-*.json;
|
|
166
|
+
scripts/ablation.py 可复现。另修复:长片场景碎片 bug(强边界最小间距)、
|
|
167
|
+
场景数随片长缩放(92min → 41 scenes / 4.6KB 地图)、ASR 进度回调。
|
|
168
|
+
|
|
169
|
+
- `cutroom cut` 真实验证 + README 真实 demo(2026-06-11,全自动模式):30s teaser
|
|
170
|
+
指令 → 2 刀 EDL 共 30.5s(13.4k chars / 19 轮),出点吸附静音;CLI cut 现产出
|
|
171
|
+
拼接 reel;README 加真实运行段落 + 字幕帧图 + receipts 摘录 + static-ffmpeg 说明;
|
|
172
|
+
LICENSE(MIT) + GitHub Actions CI(ubuntu+macos,无语音环境自动降级)就绪。
|
|
173
|
+
|
|
174
|
+
- vertical 真实验证 + 场景切分改进(2026-06-11,loop 迭代 3):vert 1080x1920 构图
|
|
175
|
+
与竖版字幕样式肉眼验证通过;build_scenes 增加自适应弱停顿增补(目标 ~90s/scene,
|
|
176
|
+
按停顿强度选边界),demo 影片 4 个等分 → 7 个语义场景;67 测试绿。
|
|
177
|
+
|
|
178
|
+
- `cutroom render` 动词(2026-06-11,loop 迭代 2):从 renders/edl.json 重渲染,
|
|
179
|
+
支持 --target / --captions / --basename 覆写;66 测试绿;真实验证 2 条秒级重渲染。
|
|
180
|
+
|
|
181
|
+
- 内层 agent 隔离(2026-06-11,loop 迭代 1):runner 加 `setting_sources=[]`
|
|
182
|
+
(SDK isolation mode)+ `output_language` 参数;65 测试绿;真实验证内层 agent
|
|
183
|
+
不再继承宿主 CLAUDE.md(英文任务答英文,1.7k chars / 5 轮)。
|
|
184
|
+
- M0 MVP(2026-06-11):log/list/map/ask/highlights/chapters/cut 全部实现;
|
|
185
|
+
65 测试全绿 + ruff 干净;真实影片(Duck and Cover, 9:15)验证:
|
|
186
|
+
ask 带帧证据引用回答(9.7k chars 预算)、highlights 出 2 条带词级烧录字幕成片
|
|
187
|
+
+ receipts.md + edl.json(14.1k chars 预算、15 轮)。
|
|
188
|
+
- ffmpeg 字幕能力解析(2026-06-11):brew 瘦身构建无 libass → `resolve_ffmpeg()`
|
|
189
|
+
三级解析 + static-ffmpeg 兜底;EDL 持久化到 renders/edl.json。
|
|
190
|
+
|
|
191
|
+
## 已知约束(来自 CLAUDE.md,不可违反)
|
|
192
|
+
|
|
193
|
+
GPU-free / 不绑定用户研究背景 / Mac 本机可跑 / 不 commit 不 push 除非用户明说。
|
cutroom-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# cutroom — 项目约束与长期记忆
|
|
2
|
+
|
|
3
|
+
## 项目定位
|
|
4
|
+
先"看完素材再下剪刀"的长视频剪辑师 Agent:本地优先、GPU-free、每一刀都附证据
|
|
5
|
+
(transcript 片段 + agent 真正看过的帧)。CLI:`log` / `highlights` / `ask` / `chapters` / `cut`。
|
|
6
|
+
|
|
7
|
+
## 硬约束(用户 2026-06-11 明确给定,不可违反)
|
|
8
|
+
- **GPU-free**:与模型训练完全无关,纯 Agent 工程。本地推理只允许 CPU/Metal
|
|
9
|
+
(faster-whisper 等),不依赖 NVIDIA/CUDA。
|
|
10
|
+
- **不绑定用户的研究背景**:本项目独立站得住,不为任何论文服务。
|
|
11
|
+
- 评判标准:真实工程量、技术 solid、能吸引注意(GitHub stars / demo 传播力)。
|
|
12
|
+
- Mac(Apple Silicon)本机可开发可运行,同时保持 Linux 兼容。
|
|
13
|
+
|
|
14
|
+
## 技术基线
|
|
15
|
+
- Python 3.12 + uv;Claude Agent SDK 0.2.x(复用 Claude Code 认证,无需单独 API key)。
|
|
16
|
+
- ffmpeg、yt-dlp、faster-whisper、SQLite + FTS5。
|
|
17
|
+
⚠️ brew 的 ffmpeg 是瘦身构建(无 libass/subtitles/drawtext)——渲染层通过
|
|
18
|
+
`render.ffmpeg.resolve_ffmpeg()` 解析:$CUTROOM_FFMPEG → 系统 ffmpeg(有字幕能力时)→
|
|
19
|
+
static-ffmpeg 兜底(pyproject 依赖,自带 libass)。
|
|
20
|
+
- 内层 agent 看帧的兜底方案:`view_frames` 落盘 JPEG + 授予内置 `Read` 工具(原生读图)。
|
|
21
|
+
|
|
22
|
+
## 核心架构原则
|
|
23
|
+
1. **Agent 永远不读完整转写** —— 只能拿到分层视频地图(chapters ← scenes ← shots ← words)
|
|
24
|
+
+ 预算化的分页检索。这是本项目的立身之本,直接针对 AgenticVBench 量化的头号失败模式
|
|
25
|
+
(Repurpose 失败的 83% 是长上下文信息丢失)。
|
|
26
|
+
2. **每一刀有证据(receipts)** —— EDL 中每个 cut 必须携带 transcript 证据段 + 已查看帧的
|
|
27
|
+
时间戳,渲染时生成 receipts 报告。
|
|
28
|
+
3. **预算账本** —— 工具结果计费,agent 可见剩余预算;工具返回值必须紧凑、可分页。
|
|
29
|
+
|
|
30
|
+
## 证据基础(选型依据,调研于 2026-06-11,详见 DESIGN.md)
|
|
31
|
+
- AgenticVBench (arXiv 2605.27705):最佳 stack 31% vs 专家 88.5%(差距 43-65pp)。
|
|
32
|
+
- VideoOdyssey (arXiv 2605.22907):模型连续推理跨度 >3min 即显著退化 → 工程层(索引/记忆/预算)有明确杠杆。
|
|
33
|
+
- 竞品定位:HKUDS/VideoAgent(748★,agentic 但要 8GB GPU + 4 家 LLM provider);
|
|
34
|
+
SamurAIGPT/AI-Youtube-Shorts-Generator(3.8k★,固定流水线非 agentic,无视觉验证);
|
|
35
|
+
商业产品(Opus Clip 等)闭源 SaaS。"本地优先 + GPU-free + 真 agentic + 索引优先 + 证据可溯"无人占位。
|
|
36
|
+
|
|
37
|
+
## 工程纪律
|
|
38
|
+
- 与用户全局 CLAUDE.md 一致:验证后才说完成;最小变更;不加废话注释。
|
|
39
|
+
- 测试不依赖网络/API:合成视频 fixture(ffmpeg testsrc + macOS `say`);
|
|
40
|
+
需要 Claude 的 e2e 用 `requires_claude` 标记。
|
|
41
|
+
- 提交信息英文,代码注释英文;面向用户的 README 英文(开源传播)。
|
cutroom-0.1.0/DESIGN.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# cutroom — Design
|
|
2
|
+
|
|
3
|
+
> A film-editor agent that **logs your footage before it cuts**.
|
|
4
|
+
> Local-first, GPU-free, and every cut ships with receipts.
|
|
5
|
+
|
|
6
|
+
## 1. Why this exists (evidence, verified 2026-06-11)
|
|
7
|
+
|
|
8
|
+
- **AgenticVBench** (arXiv 2605.27705, May 2026): frontier agent stacks score 31% vs human
|
|
9
|
+
experts' 88.5% on real video post-production tasks. **83% of Repurpose-family failures are
|
|
10
|
+
long-context information loss** — agents burn their rollout budget on full Whisper dumps and
|
|
11
|
+
repeated frame extraction, never reaching the assembly step.
|
|
12
|
+
- **VideoOdyssey** (arXiv 2605.22907): model performance peaks at <3min continuous reasoning
|
|
13
|
+
spans and degrades after — the bottleneck is structural, so an engineering layer
|
|
14
|
+
(index + memory + budget) has real leverage that bigger models alone don't fix.
|
|
15
|
+
- **Competitive gap**: HKUDS/VideoAgent (748★) is agentic but needs an 8GB GPU + 4 LLM
|
|
16
|
+
providers; AI-Youtube-Shorts-Generator (3.8k★) is a fixed pipeline (transcript-rank only, no
|
|
17
|
+
visual verification, not agentic). Commercial tools (Opus Clip) are closed SaaS.
|
|
18
|
+
Nobody ships: **local-first + GPU-free + genuinely agentic + index-first + evidence-grounded**.
|
|
19
|
+
|
|
20
|
+
## 2. The core idea
|
|
21
|
+
|
|
22
|
+
Real editors don't scrub raw footage linearly — they **log** it first (shot lists, transcripts,
|
|
23
|
+
markers), then cut from the logs, returning to the footage only to verify. cutroom encodes that
|
|
24
|
+
discipline as architecture:
|
|
25
|
+
|
|
26
|
+
1. The agent **never sees the full transcript**. It gets a compact hierarchical *video map*
|
|
27
|
+
(chapters ← scenes ← shots ← word-level transcript) and budgeted, paged search tools.
|
|
28
|
+
2. Every cut decision must carry **receipts**: transcript segment IDs + timestamps of frames the
|
|
29
|
+
agent actually viewed. Renders include a human-auditable receipts report.
|
|
30
|
+
3. An explicit **budget ledger** prices every tool call; the agent sees its remaining budget and
|
|
31
|
+
tool outputs are compact and paginated by construction.
|
|
32
|
+
|
|
33
|
+
## 3. Architecture
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/cutroom/
|
|
37
|
+
types.py # Shot, Segment, AudioEvent, Scene, Moment, Cut, EDL, Receipt
|
|
38
|
+
db.py # SQLite + FTS5 schema, CRUD, workspace layout
|
|
39
|
+
ingest/ # "logging the footage"
|
|
40
|
+
fetch.py # yt-dlp URL / local file → normalized mp4 + mono 16k wav
|
|
41
|
+
shots.py # ffmpeg scene detection → shots (fallback: fixed windows)
|
|
42
|
+
asr.py # faster-whisper word-level transcription (CPU/Metal, GPU-free)
|
|
43
|
+
audio.py # ffmpeg ebur128 + silencedetect → loudness curve, silences, music flags
|
|
44
|
+
logger.py # orchestrates ingest → populates db, builds scenes + map
|
|
45
|
+
index/
|
|
46
|
+
map.py # hierarchical video map builder (scenes from shots+pauses; 1-line scene
|
|
47
|
+
# summaries via a cheap haiku pass at log time)
|
|
48
|
+
search.py # FTS5 transcript search + budgeted paged reads
|
|
49
|
+
agent/
|
|
50
|
+
budget.py # ledger: char/token pricing per tool result, remaining-budget readout
|
|
51
|
+
tools.py # in-process MCP tools (см. §4)
|
|
52
|
+
prompts.py # editor persona + budget discipline + receipts contract
|
|
53
|
+
runner.py # ClaudeSDKClient wiring (Claude Agent SDK, reuses Claude Code auth)
|
|
54
|
+
render/
|
|
55
|
+
edl.py # EDL validation: monotonic, in-bounds, min/max, snap to word/shot boundaries
|
|
56
|
+
captions.py # word-timestamp ASS subtitles (burned via libass)
|
|
57
|
+
ffmpeg.py # EDL → mp4: trim/concat, 9:16 smart-crop (MVP: center), fades
|
|
58
|
+
receipts.py # receipts.md: per-cut why + transcript excerpt + viewed-frame thumbnails
|
|
59
|
+
cli.py # typer: log / highlights / ask / chapters / cut
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Workspace
|
|
63
|
+
`~/.cutroom/` (override: `CUTROOM_HOME`): `library.db` + `media/<video_id>/`
|
|
64
|
+
(source.mp4, audio.wav, frames/, renders/). `video_id` = short hash of source URL/path.
|
|
65
|
+
|
|
66
|
+
## 4. Agent tools (typed multimodal primitives)
|
|
67
|
+
|
|
68
|
+
| tool | returns | budget notes |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| `get_video_map()` | duration, chapter/scene tree with 1-liners, stats | cheap, cached |
|
|
71
|
+
| `search_transcript(query, limit)` | FTS hits with ±1 segment context, timestamps | per-hit cost |
|
|
72
|
+
| `read_transcript(t0, t1)` | word-timestamped span, hard cap per call | priced by span |
|
|
73
|
+
| `view_frames(timestamps \| span, n≤6)` | JPEG frames the model actually sees | most expensive |
|
|
74
|
+
| `probe_audio(t0, t1)` | loudness curve, silences, speech density | cheap |
|
|
75
|
+
| `mark_moment(t0, t1, reason, evidence)` | registers a candidate moment | free |
|
|
76
|
+
| `propose_edl(cuts)` | validated EDL or precise rejection reasons | free |
|
|
77
|
+
|
|
78
|
+
Frames reach the model either as MCP image content blocks or via file-path + built-in `Read`
|
|
79
|
+
(image-capable) — whichever proves more reliable in integration testing.
|
|
80
|
+
|
|
81
|
+
## 5. CLI verbs (MVP)
|
|
82
|
+
|
|
83
|
+
- `cutroom log <url|file>` — ingest + index; prints the video map.
|
|
84
|
+
- `cutroom highlights <video> [-n 3] [--vertical]` — agent finds top moments → rendered clips
|
|
85
|
+
with burned captions + receipts.md.
|
|
86
|
+
- `cutroom ask <video> "question"` — answer with [mm:ss] citations.
|
|
87
|
+
- `cutroom chapters <video>` — YouTube-ready chapter list.
|
|
88
|
+
- `cutroom cut <video> "instruction"` — free-form instruction → EDL → render.
|
|
89
|
+
|
|
90
|
+
## 6. Testing
|
|
91
|
+
|
|
92
|
+
- Unit tests are offline: synthetic fixture = ffmpeg `testsrc2` color segments (detectable shot
|
|
93
|
+
boundaries) + macOS `say` speech at known timestamps (Linux fallback: espeak-ng or
|
|
94
|
+
silence+tone, transcript assertions relaxed).
|
|
95
|
+
- EDL/captions/db/search/map: pure-logic tests, no media needed where possible.
|
|
96
|
+
- `requires_claude`-marked e2e: full `log → highlights` on the fixture, asserts rendered mp4
|
|
97
|
+
duration & receipts present.
|
|
98
|
+
|
|
99
|
+
## 7. Milestones
|
|
100
|
+
|
|
101
|
+
- **M0 (this session)**: full MVP — all five verbs working on a real video, tests green.
|
|
102
|
+
- **M1**: smart vertical crop (face/active-speaker tracking via lightweight CPU detector);
|
|
103
|
+
silence-trim and filler-word removal as `cut` presets.
|
|
104
|
+
- **M2**: AgenticVBench Repurpose subset run + honest scorecard in README; budget ablation
|
|
105
|
+
(map+tools vs full-transcript baseline) — the headline chart.
|
|
106
|
+
- **M3**: multi-video projects ("find this claim across my 10 lectures"), export to
|
|
107
|
+
EDL/OTIO for NLE handoff (DaVinci/Premiere).
|
cutroom-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cutroom contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|